An annotation-based persistence framework

Use J2SE 5.0 annotations to eliminate getters and setters

1 2 3 Page 2
Page 2 of 3

Maps are handled similarly, but the elements that represent the values each have a name= attribute that identifies the key (the key object's toString() method is used for this purpose). For example:

 <field_name_of_Map_field>
    <ValueType  name="value_returned_by_key_toString()_method" className="...">
        value is displayed here
    </ValueType">
</field_name_of_Map_field>

Finally, note that this example is a persistence framework, after all, so all elements that represent objects of a known class are exported with the fully qualified class name specified in the className= attribute. The field name, if known, is also output as the name= attribute. There's an example in the output file on Listing 2's Line 6.

Declaring annotations

Before we get started looking at the implementation, I must say that the declaration syntax for annotations is not very good. Too much magic stuff is going on, and many syntax examples prove inappropriate for the task at hand, but we all have to live with it.

Annotations are declared like interfaces, but with some weirdness. Listing 3 is a simple example. The interface name is the annotation name, and you must precede the word interface with an @ sign. All annotation-style interfaces magically extend the Annotation interface.

Listing 3. Persistent.java: The @Persistent declaration

  1  package com.holub.persist;
   2  
   3  import java.lang.annotation.*;
   4  
   5  @Retention(RetentionPolicy.RUNTIME)
   6  @Target( ElementType.FIELD )
   7  public @interface Persistent
   8  {   String  value()  default "";    // Won't accept null;
   9  }
  10  

The two "meta-annotations" on Lines 5 and 6 control how the annotation can be used. The various retention policies (all defined in the

java.lang.annotation.RetentionPolicy

Javadoc page) are:

RetentionPolicy.CLASS Annotation is in the classfile, but cannot be accessed at runtime. Useful for postprocessors that work directly on the classfile
RetentionPolicy.RUNTIMEAnnotation is in the classfile and can be accessed via the introspection APIs
RetentionPolicy.SOURCE Annotation used by the apt preprocessor at compile-time

The

@Target

meta-annotation specifies to which Java elements you can attach annotations. Arguments defined in

java.lang.annotation.ElementType

are, for the most type, self explanatory:

ElementType.ANNOTATION_TYPE A meta-annotation applied to other annotations
ElementType.CONSTRUCTOR A constructor
ElementType.FIELD A field
ElementType.LOCAL_VARIABLE Local variable in a method
ElementType.METHOD A method
ElementType.PACKAGE A package
ElementType.PARAMETERAn argument to a method
ElementType.TYPEA class, interface, or enum declaration

You can combine as if you were initializing an array, as follows:

 @Target( {ElementType.FIELD, ElementType.METHOD} );

Two other meta-annotations are also available, though I haven't used them here:

@DocumentedAnnotation will be documented by javadoc by default
@Inherited Annotation used on a base class will be visible via introspection of derived classes that aren't themselves annotated

Returning to Listing 3, the method names define the annotation arguments. Here, however, the magic name

value()

is used. If a method by this name is present, then you can use the form

@annotation("value")

in your code (without the

arg=

part). The method's return type controls the argument type, which must be a primitive type or a

String

. Use the

default

clause to specify a default value for this argument. If the

default

clause is missing, the argument is required.

Now let's look at the @Exportable annotation, which demonstrates an annotation with more than one argument:

Listing 4. Exportable.java: The @Exportable declaration

  1  package com.holub.persist;
   2  
   3  import java.lang.annotation.ElementType;
   4  import java.lang.annotation.Retention;
   5  import java.lang.annotation.RetentionPolicy;
   6  import java.lang.annotation.Target;
   7  
   8  @Retention(RetentionPolicy.RUNTIME)
   9  @Target( ElementType.TYPE )
  10  public @interface Exportable
  11  {   String description()    default "";
  12      String name()           default "";
  13      String value()          default "";
  14  }
  15  

Note that I've supplied default values for everything and specified a magic value() as well, so there are several possible incarnations of this annotation:

 @Exportable
@Exportable("element_name")
@Exportable(name="element_name")
@Exportable(description="Comment goes here")
@Exportable(name="element_name" description="Comment goes here")

I've implemented the code that processes this annotation to treat the second and third of these forms identically, but that's an implementation detail you don't see in the declaration.

Implementing annotations at runtime

Moving on to the implementation, the XmlExporter class in Listing 5 does all the work:

Listing 5. XmlExporter.java: The XML exporter class

1 2 3 Page 2
Page 2 of 3