Java Annotations & Annotation Processing Tool
Annotations were introduced in Java 5 to enable programmers to write code in a declarative programming style. This style of coding helps in eliminating the need to maintain ‘Side Files’, like a separate XML configuration file. By using annotations, the relevant information is declared and maintained within the source class file itself. These annotations are also known as ‘metadata’.
Creating an Annotation
Annotations are generally seen in software libraries and frameworks that are used by other developers for their software solutions. For example, Spring Framework, Jersey Framework, GSON, etc. To implement an annotation, you need to use @interface. The methods declared in the interface are the elements of the annotation type. These methods within the annotations must not have any parameters or a throws clause. The default values of the elements are defined using the “default” keyword followed by the value. The return types are also restricted to the following types:
- primitive data types (byte, char, short, int, long, float, and double)
- java.lang.String
- java.lang.Class
- enum
- annotations
- an array of above
For example, you can implement an annotation to declare a particular field as cached until expiry time.
public @interface Cached { int expireIn() default 60000; // 1 minute default value }
The expireIn element in the @Cached annotation indicates for frequently a property should be cleared from the cache. After defining the annotation type, you can use it to annotate declarations for your fields as shown below.
@Cached private Integer minuteNumber; // Cache field for one minute (default) @Cached(expireIn = 10000) private Integer tenSecondsNumber; // Cache the field for 10 seconds
You can checkout the complete source code in my github repository
Targeted Annotations
By default, an annotation can be used to declare any java program element. To restrict the use of annotations for a particular type(s) of program element, you will have to use the @Target while implementing the annotation. To make the @Cached annotation restricted to only fields, it should be implemented as follows:
import java.lang.annotation.ElementType; import java.lang.annotation.Target; @Target(ElementType.FIELD) public @interface Cached { int expireIn() default 60000; // 1 minute default value }
The @Target(ElementType.FIELD) declaration indicates that this annotation can only be used with fields. To use the annotation for more than one element type, each element type should be added within {}.
import java.lang.annotation.ElementType; import java.lang.annotation.Target; @Target({ElementType.FIELD, ElementType.PARAMETER}) public @interface Cached { int expireIn() default 60000; // 1 minute default value }
The different element types that can be targeted in Java are:
- ANNOTATION_TYPE – Annotation types.
- CONSTRUCTOR – Constructors of a class.
- FIELD – Properties of a Class.
- LOCAL_VARIABLE – A local variable within a method.
- METHOD – A method
- PACKAGE – A Package
- PARAMETER – A parameter within a method signature
- TYPE – Class, interface (including annotation type), or enum declaration
- TYPE_PARAMETER – Used in generics type parameter declarations delimited by < and > (Since Java 8).
- TYPE_USE – allows an annotation to be applied at any type use. (Since Java 8)
- MODULE – Java module declaration (since Java 9)
For examples of TYPE_PARAMETER and TYPE_USE, see github repository.
Retention of Annotations
The retention policy can be used for indicating how long the annotation type should be maintained. If no retention policy is defined in the annotation, then the retention policy defaults to RetentionPolicy.CLASS. The retention policy is declared using the @Retention annotation.
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Cached { int expireIn() default 60000; // 1 minute default value }
The @Retention(RetentionPolicy.RUNTIME) indicates that the @Cached annotation should be maintained until runtime. The different retention policies are:
- SOURCE – The annotation would be maintained only in the source file. It would be discarded at compile time.
- CLASS – The annotations would be maintained in the source and class files. It would be discarded at runtime by the JVM.
- RUNTIME – The annotations would be maintained in the source file, class file and in JVM. The annotations can be accessed using Java reflections.
The Annotation Processing Tool
The Annotation Processing Tool (apt) was introduced in java 1.5. It was a command-line utility script that can be used for processing annotations during compile time. The apt standalone tool was later deprecated in Java 7 and removed in Java 8. The annotations processing feature was seamlessly integrated with the javac compiler in Java 6 onwards. The annotations processing can be triggered using the javax.lang.model package.
We can either implement the javax.annotations.processing.Processor interface or extend the javax.annotations.processing.AbstractProcessor to write customized annotations processors. The customized annotations processor could be configured using the following annotations:
- javax.annotations.processing.SupporterAnnotationTypes – To specify which annotation types should be processed.
- javax.annotations.processing.SupportedSourceVersion – To support a maximum version of the java source. This is added as a convention to avoid troubles with handing new datatypes and features on a higher version of Java.
- javax.annotations.processing.SupportedOptions – Used to register custom parameters that are passed through the command line.
After implementing the custom annotation processors, they can be registered and configured into the build process.