Recently, I was giving an overview of Java to a bunch of C/C++ developers to help them bridge the gap. Mostly I ended up assuring them that they knew what they were doing and filled in very few gaps. I did enlighten them on some of the fun things like Collections and some confusing things like Date. We covered lots of ground in those few hours of presentation and banter, but one question I couldn’t answer was about annotations.
I had put a few code samples in my presentation that I’d pulled from a recent project. Stuff I knew worked, so I wouldn’t be surprised with any hastily thrown together code with style, format, or syntax errors. One of the classes was an annotated Spring class, with some code not too much unlike this snippet:
@Autowire private SomeBean someBean;
We went down a path of queries related to how to create new ones, which is a simple @interface declaration like the following.
public @interface Foo{ }
They queried how to put them in the code, and I pointed back to the example. I also showed them some other examples related to using them on class definitions, methods, and on parameter values, as defined by the @Target annotation and ElementType enum. We looked at examples of commonly encountered annotations such as @Deprecated and @SuppressWarning among others. We even looked at the different uses of @Retention and discussed the RetentionPolicy enum; how SOURCE is used to provide hints for the compilers (and IDEs) but aren’t retained in the compiled code, how CLASS is retained in the code but not necessarily available at runtime, and how RUNTIME is certain to be available at runtime.
Then they wanted to know how to use them. Not how to implement them, but how to access the @Retention(RetentionPolicy.RUNTIME) declared annotations.
I hated to admit that I didn’t really know. I knew there weren’t any frequently used utility classes for grabbing annotations, but that there were annotation-related methods on the reflection classes. I knew in practice that they were used, but not directly how, so I set out to learn so I could completely satisfy their questions.
To be sure, I use annotations all day long. I probably @Deprecate a method every day, sometimes permanently, sometimes just to quickly find uses as Eclipse is faster at adding them to problems than it is at searching. Any class that extends or implements another is surely fraught with @Override annotations. I probably write twice as many @Test annotated methods than anything else. I have @Autowire in nearly every function-full class of a Spring application, and every Hibernate project is filled with the @Column and all the other JPA annotations.
What I hadn’t had to do, though, was write any code to find and use annotations. I can’t even recall passing an annotation to a method to try to identify them. After much digging, I ascertained that indeed there aren’t any utility classes that aid in finding or using annotations. There’s got to be some heavy work behind the classpath scanning in frameworks like Spring and Hibernate to find annotated classes and methods. I’m not quite prepared to dive into that, but let’s look at some simple cases of using annotations.
First, a simple set of annotations should be built for our various uses. Let’s take one for each kind of element: class, method, and property.
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME)
public @interface TypeAnnotation { }
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME)
public @interface FieldAnnotation { }
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME)
public @interface MethodAnnotation { }
@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME)
public @interface ParameterAnnotation { }
There are actually eight different ElementTypes that can be used, but these are surely the most common, and are certainly enough to provide a thorough example. I’ll expand on the simple examples here, but I wanted to first discuss the annotations on our annotations.
The @Target annotation lets the compiler know where the annotation may be used. Compilers and IDEs will warn if the annotation is used on the wrong type of element. If the @Target is left off, the annotation may be applied anywhere without warning. The ElementType must be defined, and allows multiple, but not duplicate entries. Multiple ElementType allow the annotation to be used in any of those instances.
The @Retention annotation lets the compiler know how long to maintain the annotation. As mentioned, there are three RetentionPolicy values that are allowed, SOURCE, CLASS, and RUNTIME. If the @Retention annotation is used, a RetentionPolicy must be declared. If the @Retention annotation is not used, the default is CLASS, which will keep the annotation in the compiled class, but it may not be available to the runtime.
The @interface is used to define the annotation. The name of the interface is then used when putting the annotation in source later. Other than that modification to the naming, the annotation is declared much like any other interface, in that you can declare member variables and methods. It is important to note these aren’t intended for use as regular interfaces, so there are some other restrictions. Member variables must be public, static, or final, but may be of any valid type. Only public and abstract modifiers are allowed on methods. And only a small set of return types are allowed for methods; strictly, primitives, Strings, enumerations, other annotations are allowed, or 1-dimensional arrays of those types.
One more bit, if you want to make an annotation that takes a value without a name, you must declare a member variable named “value.” That is, like @SuppressWarnings(“unchecked”). It can be a single value or array, depending on your needs. If you define it as an array you can provide several values by wrapping them in squiggly-braces like @SuppressWarnings({“unchecked”,“unused”}) would do. If you do not wish to use “value,” the variable must be explicitly declared (and, truly, value is optional) such as @SuppressWarnings(value=“unchecked”).
Other differences will become apparent as the article continues, and as you play with annotations of your own. In trying to keep the article short, and not delve too much into every possible use case, I’ll show some simple uses, point out some of these other nuances of @interface definitions, and leave it to the reader to expand on that.
We’ve got some trivial annotations set up, so let’s apply them to a simple class.
@TypeAnnotation class Foo {
@FieldAnnotation public Object object;
@MethodAnnotation public Object setObject(@ParameterAnnotation final Object object){
this.object = object;
}
}
If you’ve used annotations in your code, you’ve probably used some or all of these kinds of annotations. Certainly, the annotations used before have actually done something. Before we get into doing something, let’s try to find the annotations at runtime.
I mentioned previously that Spring and Hibernate and other frameworks have functionality to scan classes in the classpath for annotations. If you’ve taken a look at how they do it (they’re both open-source projects, so dig in…) or hit a search engine looking for the answer, you probably have seen or correctly surmised that interrogating a classpath is not as easy as it seems. As such, I’m not going to try to lay that out here (sorry to whet your appetite), but instead will use the annotations in a more explicit manner.
First, a simple method to see if the annotation exists, just as an example of accessing them.
public boolean isAliased(final Class type) {
final TypeAnnotation typeAnnotation = type.getAnnotation(TypeAnnotation.class);
return (typeAnnotation != null);
}
The trivial example interrogates the provided class using the Class.getAnnotation() method. There’s a similar Class.getAnnotations() that will return an array (zero-length if empty) of the annotations if you want to check for more than one. This would be used very simply by something like the following snippets, the first returning true, and the other false.
isAliased(Foo.class);
isAliased("yes, a literal".getClass());
It isn’t a terribly useful example, so let’s make a method that does a little more. First, let’s expand one of our annotations to give us some runtime differences from source.
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME)
public @interface FieldAnnotation { String value(); }
Here we’ve changed our FieldAnnotation to require we provide some additional information in the form of a String property. It’s been named “value” for convenience, so we can use it without forcing the use of the name. Note that it looks like a method declaration, but we’ll be treating it as both a variable and a function.
If we applied the same annotation to the same class as before, we’d run into an error as we’re missing a required property. Let’s update our class a little and give this value some functionality.
@TypeAnnotation class Foo {
@FieldAnnotation("here") public Object object;
@FieldAnnotation("there") public Object other;
@MethodAnnotation public Object setObject(@ParameterAnnotation final Object object){
this.object = object;
}
}
Again, the class isn’t terribly useful, but we now have two annotated member variables with different names. Let’s make a quick method to set those values by their annotation name, ignoring their object name.
void setPropertyByAnnotationName(final Object object, final String name, final Object value) {
final Field[] fields = object.getClass().getDeclaredFields();
for (final Field field : fields) {
final FieldAnnotation fieldAnnotation = field.getAnnotation(FieldAnnotation.class);
if(fieldAnnotation!=null && name.equals(fieldAnnotation.value()){
field.set(object, value);
}
}
}
Lots of spots for error there, as there’s no null checking, it assumes the field is accessible, and the object is of the right type…but it matches our simple Foo class, so we’ll let it go for now. As with the Class, there is a Field.getAnnotations() in case you’d like to investigate more than one annotation. What we have is a method that interrogates our object (first one) to look for fields (member variables) annotated with our FieldAnnotation. When it finds one, it checks to see if the name matches the value (note it looks like a function here) in the annotation. When there’s a match, it sets the value as provided by our value parameter. This would be used simply as thus:
Foo foo = new Foo();
setPropertyByAnnotationName(foo, "here", "this is set!");
setPropertyByAnnotationName(foo, "there", Calendar.getInstance());
With this, we should end up with a Foo.object that contains the String “this is set!” and Foo.other that contains a Calendar with the current time in it.
One might desire to ask what good is that? We surely have access to Foo.object and Foo.other directly in this class, but it will certainly be the case that we might not have such access in other classes. Additionally, with the annotation, we don’t even really need to care that it’s a Foo object. We could very easily declare another class with similar annotated members and have their fields accessed with the same method we just created. This is roughly how Spring’s @Autowire works. They, of course, do a bit more in terms of recognizing names and types and whatnot without explicit definition, but this is the crux of it.
Accessing methods is done pretty much the same way. Let’s use the @MethodAnnotation to use the setter we’ve got. Note that calling methods is a lot trickier because the annotation itself has no way to ensure that the parameter list is correct. It is up to the method using reflection to determine if the prototype of the method is something it can work with. For brevity, I’ll leave this using the original annotation, which means every annotated method will be called and its value set to the same thing; the same modifications on the @FieldAnnotation can be done here for similar functionality.
void callMethodByAnnotation(final Object object, final Object value) {
final Method[] methods = object.getClass().getDeclaredMethods();
for (final Method method : methods) {
final MethodAnnotation methodAnnotation = method.getAnnotation(MethodAnnotation.class);
if(fieldAnnotation!=null){
method.invoke(object, value);
}
}
}
Again, a slew of opportunities for errors with missing null-checks, accessibility, and the mentioned parameter list validation. Also, there is a similar Method.getAnnotations() method that will return an array of annotations in case you want to check for more than one. This method simply checks the class for all methods with the right annotation, and if found, tries to invoke the method with the provided object as the parameter. Again, with this functionality you can find methods in classes without caring about their real names. This, again with more guts, is how Spring handles its @RequestMapping methods in @Controller classes. As before, this can be implemented with this simple
Foo foo = new Foo();
callMethodByAnnotation(foo, ""this is set!");
In the end, we end up with Foo.other set to a String saying “this is set!” using the annotation instead of calling the method directly.
Trickier is handling the ElementTypePARAMETER annotations. This is because there’s no direct reflection access to the parameter list. There are a few arrays that can be gathered to help identify the parameters, but the parameters are actually only realized when you use the Method.invoke(), so to use these, you need to handle all of the parameters by annotation, name, or type. A typical use would be some form of IOC or other runtime association of parameters to other values available to the application.
Why this is tricky is that there is no opportunity, except perhaps inside an invoked method, to access the parameter annotations when calling the methods directly. With that understanding, we can see that these are best paired with annotated classes or methods, where the functions are going to be called indirectly, and therefore there is time to interrogate the Method for annotations before calling Method.invoke. That said, let’s modify our other method-calling method to take a map of values to tie to our parameters.
Before we do, though, this brings about another sticky bit. Unlike classes, member variables, and methods, parameters don’t always keep their names. When classes are compiled, the parameters are reduced essentially to a type and order. The variable name associated with it is not kept with the method unless the class is compiled with debugging symbols. As such, like we did for the fields, we’ll add a required value to represent the name of the parameter. This way, the annotation will maintain the value that we’ll use to pull values from our map, and it won’t matter if debug is enabled or not; our annotation will continue to work.
@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME)
public @interface ParameterAnnotation { String value(); }
And since it’s required with no default, we’ll have to change our class appropriately.
@TypeAnnotation class Foo {
@FieldAnnotation("here") public Object object;
@FieldAnnotation("there") public Object other;
@MethodAnnotation public Object setObject(@ParameterAnnotation("inside") final Object object){
this.object = object;
}
}
And we’ll modify the method to take advantage of that annotation.
public void callMethodByAnnotation(final Object object, final Map map) {
final Method[] methods = object.getClass().getDeclaredMethods();
for (final Method method : methods) {
final MethodAnnotation methodAnnotation = method.getAnnotation(MethodAnnotation.class);
if (methodAnnotation != null) {
final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
final Object[] parameters = new Object[parameterAnnotations.length];
for (int i = 0; i < parameterAnnotations.length; i++) {
parameters[i] = null;
final Annotation[] annotations = parameterAnnotations[i];
for (final Annotation annotation : annotations) {
if (annotation instanceof ParameterAnnotation) {
parameters[i] = map.get(((ParameterAnnotation) annotation).value());
}
}
}
method.invoke(object, parameters);
}
}
}
Note the busy work necessary to grab the annotation. The annotations come from an array of arrays. The first dimension is one for each parameter, and the second dimension is the array of annotations for the parameter at that first dimension. If there are no parameters, the first dimension array is zero-length. For each parameter, if there are no annotations, that second dimension array is zero length. There is a related Method.getParameterTypes() that could be used to be certain that the type in the map was compatible with the type in the parameter, but ours is an Object, so we can be a little sloppy.
The new method can be used very much like the other, except we’ll pass the parameters in a Map. We could, of course, use any mechanism for keeping track of the things that could be put in the parameter list. If you’ve used Spring to annotate a @Controller with @RequestMethod methods, you’ll notice that you can just toss in a whole slew of parameters such as ModelMap or HttpServletRequest and it will figure them out by type.
Map map = new Map();
map.put("inside", "this is set!");
Foo foo = new Foo();
callMethodByAnnotation(foo,map);
Same caveats as before regarding shortcuts taken for brevity… Now when we call our method, it will check our map for the value. If it doesn’t find an element in the map that matches our annotation name, it puts null in the parameter list. As before, our Foo.other will have a String with the value “this is set!” when we’re done.
That seems like a lot of busy work to make sense of annotations. Really it’s a lot of fluff code around it to make it understandable. Really, the magic lies in the getAnnotations() or getAnnotation() methods on the Class and the reflection classes Field and Method, and the Method.getParameterAnnotations() method. And, of course, our trivial examples don’t carry much insight into any kind of usefulness, so let’s expand on this and make a one-class annotated XML parser. A very simple one, mind you.
Parsing XML is a pain that way too many of us go through. There are plenty of tools to help us do this, so this isn’t intended as a replacement or any kind of competition for them. It’s just familiar territory, and one I think I can squeak in a small pair of classes for a solid example of annotation use.
Consider this fairly trivial XML, where we have a Foo element with a single attribute, id, and a nested element also named Foo.
<Foo id="alpha">
<Foo/>
</Foo>
The astute among us can quickly envision a likewise trivial POJO that would do the same. I’m making public members for brevity; for real, we’d use getters and setters, right?
class Foo {
public String id;
public Foo foo;
}
With this class in mind, we can see we’ll need a couple annotations to handle identifying the class and our members.
@Target({ElementType.TYPE, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME)
@interface XMLElement { String value() default ""; }
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME)
@interface XMLAttibute { String value() default ""; }
A quick word about the two noticeable differences from the previous examples. First, we’ll note that the XMLElement has been annotated with two ElementType targets. This allows the same attribute to be used to identify types and members, as XML elements are often neatly identified with POJOs. Also, I’ve added a default to each. This is both to show what it looks like and allow us to optionally rename the item in question. A small annoyance with the defaults is that there is no null value. When the value is an array, you can declare an empty list as the default, but that’s not really null either.
@interface ArrayExampleAnnotation { String[] value() default {}; }
This allows us to alter our class to add the annotations as follows:
@XMLElement class Foo {
@XMLAttribute public String id;
@XMLElement public Foo foo;
}
We could be more explicit and annotate it thusly:
@XMLElement("Foo") class Foo {
@XMLAttribute("id") public String id;
@XMLElement("Foo") public Foo foo;
}
Or even make the POJO and XML a little less tightly coupled by changing the POJO names from the expected XML names (this is where the value makes more sense!):
@XMLElement("Foo") class FiddleStix {
@XMLAttribute("id") public String identifyingAttribute;
@XMLElement("Foo") public FiddleStix next;
}
Now we just need a function to digest our XML string and populate our POJO. To be short and sweet, and not emphasize the XML processing too much, I’m just going to use the typical W3C DOM classes that come with the Java runtime. Easy, short-ish.
public static POJO turnXMLStringIntoPOJO(final String string, final Class type) {
final POJO pojo = type.newInstance();
final XMLElement topXmlElement = type.getAnnotation(XMLElement.class);
final Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder()
.parse(new ByteArrayInputStream(string.getBytes()));
if (document.getNodeName().equals(topXmlElement.value())) {
final NamedNodeMap namedNodeMap = document.getAttributes();
for (int i = 0; i < namedNodeMap.getLength(); i++) {
final Node node = namedNodeMap.item(i);
String name = node.getNodeName();
final Field[] fields = type.getDeclaredFields();
for (final Field field : fields) {
final XMLAttribute xmlAttribute = field.getAnnotation(XMLAttribute.class);
if (xmlAttribute != null) {
if (("".equals(xmlAttribute.value()) && field.getName().equals(name))
|| xmlAttribute.value().equals(name)) {
field.set(pojo, node.getNodeValue());
}
}
}
}
final NodeList nodeList = document.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
final Node node = nodeList.item(i);
final String name = node.getNodeName();
final Field[] fields = type.getDeclaredFields();
for (final Field field : fields) {
final XMLElement xmlElement = field.getAnnotation(XMLElement.class);
if (xmlElement != null) {
if (("".equals(xmlElement.value()) && field.getName().equals(name))
|| xmlElement.value().equals(name)) {
final Object object = field.getType().newInstance();
field.set(pojo, object);
}
}
}
}
}
return pojo;
}
With a little recursion or other care, this simple method could be used to handle nesting our XML so that Foo could have Foo wtih Foo containing Foo… But for our purposes, this meets our needs. Again, much with the caveats of missing care exchanged for brevity. Notice it’s a generic class, giving us flexibility to just send it any old class. With our caveats, it makes assumptions that the class is annotated (or null pointer exceptions will occur). The string is quickly (since it’s small) into a DOM tree, and we make two passes through it, once for the attributes and once for the elements. We compare the names of the nodes to the names (value) of our annotations, if they’re defined, or to the name of the field, if the value is empty. If a match is found for the annotation, we set the value to whatever the XML contained. If a match is contained for the element, we simply instantiate the right type and add it to our object. We could explore the DOM deeper and set its values, but that’s a little larger example.
This class could take all three of our example classes (careful, as two of the classes will conflict) and parse the same XML and get the expected results.
String xmlString = "<Foo id="alpha"><Foo /></Foo>";
Foo foo = turnXMLStringIntoPOJO(xmlString, Foo.class);
FiddleStix fiddleStix = turnXMLStringIntoPOJO(xmlString, FiddleStix.class);
What should result is that our objects would have an id String of “alpha” and their same-type member populated with an empty, but instantiated item. Both classes, from the same XML just because of the annotations.
And that, my friends, is how you use annotations. Well, one way…