Taming Java Annotations.

Annotations were introduced in Java 5 mostly known as Tiger.  More than 5 years now. A lot of frameworks like Spring, Hibernate ,EJB are using annotations a feature of Java. We have used Spring MVC in our project and we are using Spring annotations. One of my colleague asked me how annotations worked. I was about to explain this but the maximum I was About to tell him was that annotations work as interface and implementation is provided by Java at run time. But then I thought what if I am going to have my own custom annotations who will handle them and who will provide required features for those annotations.

Then I thought of working on annotations at ground level and write an article on that. You can search on internet for these kind of articles and you will find a lot of articles. But I wanted to make it as simple as possible.

Prerequisites :

  • Reflection API:  Reflection API is heavily used these days in Java frameworks and Java technologies. In case you want to go through Reflection API here is a very good article.
    Java provides some basic annotations which I am sure most of the developers are using in their daily programming practices:

  • *@override : *This annotation makes sure that a method where we have annotated this annotation must override that method from the parent class.

  • *@SuppressWarnings: *This is widely used annotation for example in Java 5 if you will define a non-parameterized collection compiler will give warning you can suppress those warnings using this     annotation.
  • *@Deprecated: *This shows methods that are deprecated.
    Annotations are basically defined just like interface in Java the only difference is the keyword @ before interface
    so for example we have to define an annotation called Updatable which indicates whether an object is updatable or not
    we will define like this :
Public  @interface Updatable{  
}

This is not the complete definition so keep reading there is plenty of stuff coming!

Note:  Make sure you read comments in the code.

I have implemented a very simple example where I have used annotation which can be used to convert a java object to xml file. I have two annotations XMLObject which is applied to Target Type and another called XMLTag which will be applicable for fields in that object as shown below.

package com.pawan.annotations;  
import java.lang.annotation.Documented;  
import java.lang.annotation.ElementType;  
import java.lang.annotation.Retention;  
import java.lang.annotation.RetentionPolicy;  
import java.lang.annotation.Target;  
/*  
 * This annotation is used for objects to be declared as XML  
 * Object. It has a retention policy of Runtime.  
 * {@link Retention} determines the scope of the annotation  
 * so {@link RetentionPolicy# RUNTIME} says that  
 * annotation will retain till Runtime. {@link Target}  
 * determines the types on which this will be applicable.  
 * {@link ElementType#Type}is an <b>enum</b> which has about  
 * 8 types where annotations can be applied.Type is Class.  
 * {@link Documented} that annotations with a type are to  
 * be documented by javadoc and similar tools by default.  
 * At run time the value field will be used as a header tag  
 * name for {@link XMLTag}. This is a sample annotation  
 * which can be extended to create a Object to XML converter  
 * using annotations.  
 *  
 * @author Pawan Chopra  
 *  
 */  
 @Documented@Retention(RetentionPolicy.RUNTIME)  
 @Target(ElementType.TYPE)  
 public @interface XMLObject {  
 /*  
  * This is a field but acts as a function  
  * default value is header. It returns an array of  
  * String values. You can have multiple field or you can say  
  * methods like this.  
  * @return {@link String[]}  
  */  
   public String[] value() default "header";  
}  

 

package com.pawan.annotations;  
import java.lang.annotation.Documented;  
import java.lang.annotation.ElementType;  
import java.lang.annotation.Retention;  
import java.lang.annotation.RetentionPolicy;  
import java.lang.annotation.Target;  

/*  
 *  
 * This annotation is used for fields to be declared  
 * as XML tags under {@link XMLObject}.  
 * It has a retention policy of Runtime.  
 *  
 *  
 * @author Pawan Chopra  
 *  
 */  
@Documented  
@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.FIELD)  
public @interface XMLTag {  
 /*  
  *  
  * @return {@link String[]}  
  */  
  public String[] value() default "";  
}  

Let’s  go through it :

As explained earlier annotations are defined just like interface except the keyword @ before interface.  Rest of things are defined below:

  1. @Retention :- *This is a very important thing related to Annotations. This defines the scope of annotation. That means at till what level annotation will retain(consider). @Retention annotation defines the Retention policy for a particular annotation. *AnnotationPolicy is an enum provided by Java which has values like COMPILE,  CLASS, RUNTIME etc.  So for example RUNTIME says that this annotation will retain till Runtime.
  2. @Target :- *This is another annotation which will be applied to  shows that a annotation will be applicable to the defined types only it can have multiple values. *ElementType is an enum which has about 8 types on which annotations can be applied for example: FIELD, PARAMETER, METHOD, TYPE(This is equal to class).
  3. *@Documented :- *This shows that annotation must be documented by tools like javadoc.
  4. value is a filed as well as method it is bit confusing but this is how annotations works in java. For example in our case we have a value field which will be used as a method and will return a array of Strings the default value is “header”.
    We have an object below which is using these annotations this has a single property which is of String type.
package com.pawan.xmlobject;  

import com.pawan.annotations.XMLObject;  
import com.pawan.annotations.XMLTag;  
/*  
 *  
 *  
 * This is a sample class that needs to me  
 * converted to XML. Annotation is not a java  
 * declarations so it does not end with semicolon.  
 *  
 *  
 *@author Pawan Chopra  
 */  
@XMLObject("Task")  
public class Task {  
   @XMLTag("name") private String taskName = "Read about Design Patterns.";  
}  

Below class is the heart of the annotations which handles annotations at runtime and make fulfill their purpose.

package com.pawan.annotationhandler;  
import java.io.PrintWriter;  
import java.lang.reflect.Field;  
import com.pawan.annotations.XMLObject;  
import com.pawan.annotations.XMLTag;  
/*  
*  
*  
* This class is the heart of annotations. This is  
* the class which makes annotations valuable in Java.  
* I have written this for annotations. I have defined  
* in case you want to create your own Annotations you  
* need to have a handler like this which can compile  
* your annotaions in a useful way that too at run time  
* (Remember we have a retention policy of Runtime?). This  
* class has a heavy use of reflection which is used to  
* do alot of operations at run time. In case you are not  
* familiar with Reflection api of Java  
* This class basically use {@link Class#getAnnotation(Class)}  
* method of {@link Class}. This gives you access to the  
* annotation object at run time then you can class defined  
* methods on that object and fetch the values.  
* This class has mainly two methods one for handling  
* object level annotations and other for handling field  
* level annotations. You can extend this class for your own  
* purpose for example for handling diffrent type of fields  
* and also methods.  
*  
*  
*@author Pawan Chopra  
*/  
public class AnnotationHandler {  
 private PrintWriter out;  
 public AnnotationHandler(PrintWriter out) {  
 this.out = out;  
 }  

 /*  
 *  
 * This method handles the object level annotations.  
 * @param object  
 */  
 private void handleObjectLevelAnnotation(Object object){  
 XMLObject xmlObjectAnnotation = (XMLObject) object.getClass().getAnnotation(XMLObject.class);  
 if( xmlObjectAnnotation == null ){  
 out.print("Nothing to be flushed.");  
 }  
 else{  
 String[] className = xmlObjectAnnotation.value();  
 if(className.length>0){  
 out.println("<"+className[0]+">");  
 handleFieldLevelAnnotation(object);  
 out.println("</"+className[0]+">");  
 out.flush();  
 }  
 }  
 }  

 /*  
 *  
 * This methods handles the field level annotations.  
 * @param object  
 */  
 private void handleFieldLevelAnnotation(Object object){  
 Field[] fields = object.getClass().getDeclaredFields();  
 for (int i = 0; i < fields.length; i++) {  
 fields[i].setAccessible(true); // Make private fields accessible.  
 XMLTag xmlTag = ( XMLTag ) fields[i].getAnnotation( XMLTag.class );  
 if(xmlTag==null){  
 continue;  
 }  
 else{  
 String[] fieldName = xmlTag.value();  
 if(fieldName.length>0){  
 out.print("\t<"+fieldName[0]+">");  
 try {  
 /*  
 * I have only one field in Task class you can have multiple  
 * so in that case check for the field type and use if else  
 * or switch case to handle multiple fields.  
 */  
 out.print(((String)fields[0].get(object)));  
 } catch (IllegalArgumentException e) {  
/* TODO Do something really better here or wait for my next Article!*/  
 e.printStackTrace();  
 }  
 catch (IllegalAccessException e) {  
/* TODO Do something really better here or wait for my next Article!*/  
 e.printStackTrace();  
 }  
 out.println("</"+fieldName[0]+">");  
 }  
 }  
}  
}  
/*  
*  
* This is just a start up method which is exposed to the client for  
* starting the conversion.  
* @param object  
*/  
public void provideOutput(Object object){  
handleObjectLevelAnnotation(object);  
}  
}  

Let’s go through the class:

This class has 3 methods and a constructor which takes a PrintWriter object as input parameter.  This object is used to write output at required destination. The main method which is exposed to the client is provideOutput(Object obj ) *method which takes Object as an input argument this is the object where annotations are applied In our case it is *Task object. This object doesn’t perform any operation it simply call a private method called *handleObjectLevelAnnotation(Object object) *and pass the object to this method. This method use Class object provided by Java Api to get declared annotations. Class object  is the heart of Reflection API. At runtime this provides you declared methods and fields etc for a particular object. Similarly it has a method callet getAnnotation(Class) which takes the type of annotation you want to get from a particular object. In our case we are getting XMLObject which is declared at Object level.

This will get value from that object and process it. Then this will call another method * handleFieldLevelAnnotation(object). *This methods handles annotations at field level. This works in similar way as our first method.

Below is the test class of AnnotationHandler.

package com.pawan.annotationhandler;  
import java.io.PrintWriter;  
import com.pawan.xmlobject.Task;  
public class AnnotationHandlerTest {  
 public static void main(String[] args){  
    Task task = new Task();  
    AnnotationHandler annotationHandler = new AnnotationHandler  
    ( new PrintWriter( System.out ) );  
    annotationHandler.provideOutput(task);  
  }  
}  

You can download the code here. Compile and Run it. This is a very simple example play with it make changes and try other things like method level annotations. Comments are explaining a lot of things so make sure you read them.

I will be looking for feedback on this article thanks!

Share on : Twitter, Facebook or Google+