In one sententce, Annox allows you to load Java annnotations from XML resources instead reading them directly from packages, classes, fields, constructors and methods.
Java 1.5 introduced annotations, a general-purpose facility which allows you associate structured metadata with Java constructs (packages, classes, fields and so on). Java annotations are reflective in that they are embedded in class files generated by the compiler and may be retained by the Java VM to be made retrievable at run-time.
Java annotations is a very powerful and at the same time easy-to-use feature of the Java language. However, the fact that Java annotations must be defined and maintained in the source code of the program enforces certain limitations on the usage of annotations:
- You can't annotate third-party classes.
- You can't provide alternative annotations for already annotated classes.
- You can't provide different annotations for the same class.
Annox addresses these issues by providing a utilities which can load Java annotations from XML resources. Here's a small example.
Let's start with a normal annotation usage. Consider the following annotated class:
Typically you would read annotation from this class as follows:
Now assume that
MyClass is a third-party class which can't be modified that easily. In this case you can't read annotations directly from
MyClass or its fields, methods and so on. Here's how you can solve the task with Annox.
Annox reads annotations from XML resources associated with classes and packages. By default, class name is simply suffixed with the suffix
.ann.xml. Default annotations resource for
org.jvnet.annox.demos.guide.MyClass will be
xmlns:g="http://annox.dev.java.net/org.jvnet.annox.demos.guide" associates the prefix
g with the package
org.jvnet.annox.demos.guide. Thus the element
g:MyAnnotation defines an annotation of class
org.jvnet.annox.demos.guide.MyAnnotation. The rest is quite self-explanatory.
The Annox-style code for reading annotations is just slightly different:
Java annotations as XML elements
Annox is based on a very simple observation: Java annotations can be elegantly expressed as XML elements. Consider once again example from the introduction:
Even without any explanation you can easily read the following Java annotation:
Now, to be more specific, here are the rules:
- Namespace declaration associates namespace URI with package. In the example above
http://annox.dev.java.net/org.jvnet.annox.demos.guidepoints to the package
http://annox.dev.java.net/is a standard prefix.
- Local name of the XML element defines local name of the annotation class. The
MyAnnotationas local class name.
- Consequently, qualified name of the XML element provides the fully qualified name of the annotation class (
- Alternatively you can use
annox:classattribute to provide the fully qualified name of the annotation class. This is useful if, for instance, Java class is not a valid XML name (like
- Values of annotation fields can be declared as attributes or as sub-elements. Simply use name of the field as local name of the attribute or sub-element.
- You may also use
annox:fieldattribute of the sub-element to provide the name of the field. This is useful if you can't use the respective field name as the name of the XML element for some reasons.
- If your annotation has a sole
valuefield, you can include the annotation value directly, without the surrounding
Following these rules you can express virtually any Java annotation in XML. And the best thing is that Annox can parse such XML elements and restore annotation instances automatically.
Let me give you a more extensive example. Consider the following annotation classes
org.jvnet.annox.parser.tests.B$C (inner class of
Annotation classes presented above list almost all of the definition possibilities of annotation: primitive, string, enum, class and annotation fields with single or array cardinality. Here's an example of how an instance of the annotation
org.jvnet.annox.parser.tests.A can be expressed in XML:
The corresponding annotation definition in Java would be as follows:
I hope you see how simple it is. Qualified name of the XML element points to the annotation class, attributes and sub-elements define values for annotation fields. And I'd like to repeat it once again:
Laconic syntax for single-element annotations
If your annotation has a single
value field, you can use a laconic syntax when declaring it in your Java class. Assume you have the following annotation:
Verbose declaration syntax would be:
Java allows you to shorten it as follows:
Accordingly, with Annox you can use the verbose declaration:
As well as laconic syntax:
Using the annotated element factory
Java defines a special annotated element interface (
java.lang.reflect.AnnotatedElement) which allows annotations to be read reflectively. This interface is implemented by
java.lang.reflect.Method. This provides access to the annotations of the corresponding program element.
Annotated element factory introduces an additional abstraction layer. Instead of using packages, classes and so on as annotated elements directly, we may use the annotated element factory to load (read, construct, etc.) annotated element for the element of your program.
Using an intermediate factory allows you to implement an alternative strategy for loading annotations for your program elements. Here's a small example.
In the first case we use the class
MyClass.class as annotation element. In this case we'll be able to access only those annotations which were defined in this class.
In the second case we use the annotation factory to read the annotated element for our class. In this case the factory has a chance to implement a different strategy for loading annotations. For instance,
DualAnnotatedElementFactory used in this example will first check for annotations in corresponding XML resources and then, if no such annotations found, check the original program element (package, class, etc.) for annotations.
Since annotation access methods are defined in the
AnnotatedElement interface, the way we access annotations of the annotated element is identical in both cases. Thus, usage of annotated element factory is non-intrusive for the rest of the code, introduces almost no overhead, but adds a degree of freedom.
Annox provides three implementations of the
DirectAnnotatedElementFactory- trivial implementation, returns program elements as annotated elements.
ResourcedAnnotatedElementFactory- reads annotations of the annotated elements from XML resources.
DualAnnotatedElementFactory- contains two further annotated element factories - primary and secondary. This implementation first tries to read an annotation with the primary factory. If primary factory returns
null, dual factory tries the secondary factory. By default, dual factory is configered with resourced factory as primary and direct factory as secondary factory.
Since direct and dual annotated element factories are trivial, further sections of this guide concentrate on the resourced factory.
Reading annotated elements from XML resources
In one of the previous sections we saw that Java annotations can be expressed in the form of XML elements. XML can also be used to associate such XML elements with target program items (packages, classes, etc.). Resourced factory is based on these two considerations. It loads XML annotation descriptors for packages and classes from the corresponding classpath resources, parses them and returns
AnnotatedElement instances loaded from these resources.
By default, correspondence between packages, classes and XML annotation descriptors is defined by the simple convention:
It's as easy as
Program elements are described in XML annotation descriptors using the
parameter element of the
http://annox.dev.java.net namespace. Blow is a pseudo-DTD definition of these elements:
The following two sections demonstrate the usage of these elements.
Defining class annotations in XML resources
To demonstrate XML definition for class annotations, consider the following class:
This class has a
Let's annotate this class, its fields, constructors, methods and parameters with the following trivial
According to the default convention, XML annotation descriptor for our
org.jvnet.annox.demos.guide.DemoClass must be defined in the
org/jvnet/annox/demos/guide/DemoClass.ann.xml resource. Below is the listing of this annotation resource with explanation comments inline:
Defining package annotations in XML resources
Just as we defined annotations for the
org.jvnet.annox.demos.guide.DemoClass in the
org/jvnet/annox/demos/guide/DemoClass.ann.xml resource, annotations for the
org.jvnet.annox.demos.guide package can be defined in
Package definitions may also contain definitions for classes:
In order to improve performance, Annox reader for XML annotation resources also implements caching. That is, when annotations for packages or classes are loaded for the first time, they will be cached internally and further on retrieved from the cache. The cache is based on weak hash maps to allow classes to be unloaded an avoid memory leaking.