Click here to Skip to main content
15,884,725 members
Articles / Web Development

Processing Java Annotations Using Reflection

Rate me:
Please Sign up or sign in to vote.
5.00/5 (2 votes)
17 Sep 2014CPOL4 min read 11.4K   4   1
How to process Java annotations using Reflection

In my previous article covering Java Annotations, I outlined a recent use case and provided you with some examples of custom annotations and how they might be used.

In this article, I’m going to take that a step further and give you a few examples of custom annotations and how you would process these custom annotations using the Java Reflection API. Once you have gone through this tutorial, you should come away with a better understanding of the simplicity and flexibility that custom annotations can provide. So let’s dig into the code!

Custom Annotation Listings

I have created three different annotations for the example code today which are the DoItLikeThis, DoItLikeThat and DoItWithAWhiffleBallBat annotations. Each annotation targets a different element type and has slightly different properties so that I can show you how to look for and process them accordingly.

DoItLikeThis Annotation

The DoItLikeThis annotation is targeted for the ElementType TYPE, which makes it only available for Java types. This annotation has the three optional elements description, action, and a boolean field shouldDoItLikeThis. If you don’t provide any values for these elements when using this annotation, they will default to the values specified.

Java
package com.keyhole.jonny.blog.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Annotation created for doing it like this.
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DoItLikeThis {

	/**
	 * @return - The description.
	 */
	String description() default "";

	/**
	 * @return - The action.
	 */
	String action() default "";

	/**
	 * @return - Should we be doing it like this.
	 */
	boolean shouldDoItLikeThis() default false;
}

DoItLikeThat Annotation

The DoItLikeThat annotation is an annotation that is targeted for Java fields only. This annotation also has a similar boolean element named shouldDoItLikeThat, which doesn’t specify a default value and is therefore a required element when using the annotation. The annotation also contains an element defined as a String array which will contain a list of user roles that should be checked.

Java
package com.keyhole.jonny.blog.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Annotation created for doing it like that
 * instead of like this.
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DoItLikeThat {

	/**
	 * @return - Should we be doing it like that.
	 */
	boolean shouldDoItLikeThat();

	/**
	 * @return - List of user roles that can do it like that.
	 */
	String[] roles() default{};
}

DoItWithAWhiffleBallBat Annotation

The DoItWithAWhiffleBallBat annotation is targeted for use with methods only and similar to the other annotations. It also has a similar boolean element, this one is named shouldDoItWithAWhiffleBallBat. There is also another element defined which makes use of a WhiffleBallBat enum that defines the different type of whiffle ball bats that are available for use, defaulting to the classic yellow classic whiffle ball bat.

Java
package com.keyhole.jonny.blog.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * When you can't do it like this or do it like that,
 * do it with a whiffle ball bat.
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DoItWithAWhiffleBallBat {

	/**
	 * @return - Should we be doing it with a whiffle ball bat.
	 */
	boolean shouldDoItWithAWhiffleBallBat() default false;

	/**
	 * @return - Sweet, which type of whiffle ball bat?
	 */
	WhiffleBallBat batType() default WhiffleBallBat.YELLOW_PLASTIC;
}

Annotated Classes

Now that we have our annotations defined for our example, we need a couple of classes to annotate. Each class provides example uses of the annotations with elements specified as well as relying on the default values. There are also additional fields and methods included that are not annotated and therefore should not be processed by the annotation processor. Here is the source code for the two example classes:

AnnotatedOne Class

Java
package com.keyhole.jonny.blog.annotations;

import java.util.Date;

@DoItLikeThis
public class AnnotatedOne implements AnnotatedClass {

	@DoItLikeThat(shouldDoItLikeThat = false)
	private String field1;

	@DoItLikeThat(shouldDoItLikeThat = true, roles = { "admin", "root" })
	private String field2;

	private String field3;
	private Date dateDoneLikeThis;

	/* setters and getters removed for brevity */

	@DoItWithAWhiffleBallBat
      (batType = WhiffleBallBat.BLACK_PLASTIC, shouldDoItWithAWhiffleBallBat = true)
	public void doWhateverItIs() {
		// method implementation
	}

	public void verifyIt() {
		// method implementation
	}
}

AnnotatedTwo Class

Java
package com.keyhole.jonny.blog.annotations;

import java.util.Date;

@DoItLikeThis(action = "PROCESS", shouldDoItLikeThis = true, 
      description = "Class used for annotation example.")
public class AnnotatedTwo implements AnnotatedClass {

	@DoItLikeThat(shouldDoItLikeThat = true)
	private String field1;

	@DoItLikeThat(shouldDoItLikeThat = true, roles = { "web", "client" })
	private String field2;

	private String field3;
	private Date dateDoneLikeThis;

	/* setters and getters removed for brevity */

	@DoItWithAWhiffleBallBat(shouldDoItWithAWhiffleBallBat = true)
	public void doWhateverItIs() {
		// method implementation
	}

	public void verifyIt() {
		// method implementation
	}
}

Processing Annotations

Processing annotations using reflections is actually quite simple. For each of the element types that you can create for and apply annotations to, there are methods on those elements for working with annotations. The first thing you will need to do is inspect the element to determine if there are any annotations or check to see if a particular annotation exists for the element.

Each of the element types Class, Field, and Method all implement the interface AnnotatedElement, which has the following methods defined:

  • getAnnotations() – Returns all annotations present on this element, which includes any that are inherited.
  • getDeclaredAnnotations() – Returns only the annotations directly present on this element.
  • getAnnotation(Class<A> annotationClass) – Returns the element’s annotation for the specified annotation type, if not found this returns null.
  • isAnnotation() – Returns true if the element being inspected is an annotation.
  • isAnnotationPresent(Class<? Extends Annotation> annotationClass) – Returns true if the annotation specified exists on the element being checked.

When processing our annotations, the first thing we will want to do is check to see if the annotation is present. To do this, we’ll wrap our annotation processing with the following check:

Java
if (ac.getClass().isAnnotationPresent(DoItLikeThis.class)) {
    // process the annotation, "ac" being the instance of the object we are inspecting
}

Once we have found the annotation we are looking for, we will grab that annotation and do whatever processing we want to do for that annotation. At this point, we’ll have access to the annotations’ elements and their values. Notice there are not any getters or setters for accessing the elements of the annotation.

Java
DoItLikeThis anno = ac.getClass().getAnnotation(DoItLikeThis.class);
System.out.println("Action: " + anno.action());
System.out.println("Description: " + anno.description());
System.out.println("DoItLikeThis:" + anno.shouldDoItLikeThis());

For fields and methods, checking for present annotations will be slightly different. For these types of elements, we’ll need to loop through all of the fields or methods to determine if the annotation exists on the element. You will need to get all of the fields or methods from the Class, loop through the Field or Method array, and then determine if the annotation is present on the element. That should look something like this:

Java
Field[] fields = ac.getClass().getDeclaredFields();
for (Field field : fields) {
    if (field.isAnnotationPresent(DoItLikeThat.class)) {
        DoItLikeThat fAnno = field.getAnnotation(DoItLikeThat.class);
        System.out.println("Field: " + field.getName());
        System.out.println("DoItLikeThat:" + fAnno.shouldDoItLikeThat());
        for (String role : fAnno.roles()) {
            System.out.println("Role: " + role);
        }
    }
}

Conclusion

As you can see, creating your own annotations and processing them is fairly simple. In the examples I have provided, we are simply outputting the values of the elements to the console or log. Hopefully, you can see the potential use of these and might actually consider creating your own in the future. Some of the best uses I’ve seen for annotations are where they replace some configuration code or common code that gets used often, such as validating the value of a field or mapping a business object to a web form.

And finally, here is the full source code along with a simple Java main class to execute the code:

AnnotatedClassProcessor

Java
package com.keyhole.jonny.blog.annotations;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class AnnotatedClassProcessor {

	public void processClass(AnnotatedClass ac) {
		System.out.println("------Class Processing Begin---------");

		System.out.println("Class: " + ac.getClass().getName());
		if (ac.getClass().isAnnotationPresent(DoItLikeThis.class)) {
			// process the annotation, "ac" being the instance of the object we are inspecting
			DoItLikeThis anno = ac.getClass().getAnnotation(DoItLikeThis.class);
			System.out.println("Action: " + anno.action());
			System.out.println("Description: " + anno.description());
			System.out.println("DoItLikeThis:" + anno.shouldDoItLikeThis());

			System.out.println("------Field Processing---------");
			Field[] fields = ac.getClass().getDeclaredFields();
			for (Field field : fields) {
				if (field.isAnnotationPresent(DoItLikeThat.class)) {
					DoItLikeThat fAnno = field.getAnnotation(DoItLikeThat.class);
					System.out.println("Field: " + field.getName());
					System.out.println("DoItLikeThat:" + fAnno.shouldDoItLikeThat());
					for (String role : fAnno.roles()) {
						System.out.println("Role: " + role);
					}
				}
			}

			System.out.println("------Method Processing---------");
			Method[] methods = ac.getClass().getMethods();
			for (Method method : methods) {
				if ( method.isAnnotationPresent(DoItWithAWhiffleBallBat.class)) {
					DoItWithAWhiffleBallBat mAnno = method.getAnnotation(DoItWithAWhiffleBallBat.class);
					System.out.println("Use WhiffleBallBat? " + mAnno.shouldDoItWithAWhiffleBallBat());
					System.out.println("Which WhiffleBallBat? " + mAnno.batType());
				}
			}
		}
		System.out.println("------Class Processing End---------");
	}
}

RunProcessor

Java
package com.keyhole.jonny.blog.annotations;

public class RunProcessor {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		AnnotatedClassProcessor processor = new AnnotatedClassProcessor();
		processor.processClass(new AnnotatedOne());
		processor.processClass(new AnnotatedTwo());
	}
}

— Jonny Hackett, asktheteam@keyholesoftware.com

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Keyhole Software
United States United States
Keyhole is a software development and consulting firm with a tight-knit technical team. We work primarily with Java, .NET, and Mobile technologies, specializing in application development. We love the challenge that comes in consulting and blog often regarding some of the technical situations and technologies we face. Kansas City, St. Louis and Chicago.
This is a Organisation

3 members

Comments and Discussions

 
QuestionImportant question Pin
Member 21205920-May-17 7:54
Member 21205920-May-17 7:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.