Changing Annotation Parameters At Runtime

1. Overview

Annotations, a form of metadata which you can add to Java code. These annotations can be processed at compile time and embedded to class files or can be retained and accessed at runtime using Reflection.

In this article, we will discuss how to change annotation value at runtime using Reflection. We will use class-level annotation for this example.

2. Annotation

Java allows creating new annotations using existing ones. In the simplest form, an annotation is represented as @ symbol followed by annotation name:

@Override

Let’s create our own annotation Greeter:

@Retention(RetentionPolicy.RUNTIME)
public @interface Greeter {    
    public String greet() default ""; 
}

Now, we will be creating a Java class Greetings which uses the class-level annotation:

@Greeter(greet="Good morning")
public class Greetings {}

Now, we will access annotation value using reflection. Java class Class provides a method getAnnotation to access annotations of a class:

Greeter greetings = Greetings.class.getAnnotation(Greeter.class);
System.out.println("Hello there, " + greetings.greet() + " !!");

3. Alter Annotation

Java class Class maintains a map for managing annotations – Annotation class as keys and Annotation object as value:

Map<Class<? extends Annotation>, Annotation> map;

We will update this map to alter annotation at runtime. Approach to access this map differs in various JDK implementation. We will discuss it for JDK7 and JDK8.

3.1. JDK 7 Implementation

Java class Class has field annotations. As this is a private field, to access it, we have to set accessibility of the field to true. Java provides method getDeclaredField to access any field by its name:

Field annotations = Class.class.getDeclaredField(ANNOTATIONS);
annotations.setAccessible(true);

Now, let’s get access to annotation map for class Greeter:

 Map<Class<? extends Annotation>, Annotation> map = annotations.get(targetClass);

Now, this is the map which contains information about all annotations and their value object. We want to alter Greeter annotation value which we can achieve by updating annotation object of Greeter class:

map.put(targetAnnotation, targetValue);

3.2. JDK 8 Implementation

Java 8 implementations stores annotations information inside a class AnnotationData. We can access this object using the annotationData method. We will set accessibility for the annotationData method to true as it is a private method:

Method method = Class.class.getDeclaredMethod(ANNOTATION_METHOD, null);
method.setAccessible(true);

Now, we can access annotations field. As this field also a private field, we will set accessibility to true:

Field annotations = annotationData.getClass().getDeclaredField(ANNOTATIONS);
annotations.setAccessible(true);

This field has annotations cache map which stores annotation class and value object. Let’s alter that:

Map<Class<? extends Annotation>, Annotation> map = annotations.get(annotationData); 
map.put(targetAnnotation, targetValue);

4. Application

Let’s take this example:

Greeter greetings = Greetings.class.getAnnotation(Greeter.class);
System.err.println("Hello there, " + greetings.greet() + " !!");

This will be greeting “Good morning” as that is the value we provided to annotation.
Now, we will create one more object of Greeter type with value as “Good evening”:

Greeter targetValue = new DynamicGreeter("Good evening");

Let’s update the annotation map with the new value:

alterAnnotationValueJDK8(Greetings.class, Greeter.class, targetValue);

Let’s check greeting value again:

greetings = Greetings.class.getAnnotation(Greeter.class);
System.err.println("Hello there, " + greetings.greet() + " !!");

It will greet as “Good evening”.

5. Conclusion

Java implementations use two data field to store annotation data: annotationsdeclaredAnnotations. The difference between these two: first store annotations from parent classes also and later one stores only for current class.

As the implementation of getAnnotation differs in JDK 7 and JDK 8, we using here annotations field map for simplicity.

And, as always, the source code of implementation is available over on Github.

Related posts:

Java Convenience Factory Methods for Collections
Java Program to Find Minimum Number of Edges to Cut to make the Graph Disconnected
Java Program to Implement Insertion Sort
The Spring @Controller and @RestController Annotations
Java Program to Implement WeakHashMap API
The XOR Operator in Java
Java Program to Implement Flood Fill Algorithm
Spring Boot - Introduction
Format ZonedDateTime to String
Java Program to Perform the Unique Factorization of a Given Number
Java Program to Find the Longest Subsequence Common to All Sequences in a Set of Sequences
Java Program to Implement ConcurrentLinkedQueue API
Form Validation with AngularJS and Spring MVC
Disable Spring Data Auto Configuration
Display Auto-Configuration Report in Spring Boot
Jackson Unmarshalling JSON with Unknown Properties
Java Program to Implement Pagoda
OAuth2 for a Spring REST API – Handle the Refresh Token in AngularJS
Java Program to Emulate N Dice Roller
A Guide to Java HashMap
Java Program to Implement Hash Tables with Linear Probing
Generate Spring Boot REST Client with Swagger
Multi Dimensional ArrayList in Java
Java Multi-line String
Create Java Applet to Simulate Any Sorting Technique
Java Program to Solve Set Cover Problem assuming at max 2 Elements in a Subset
Spring Boot - Zuul Proxy Server and Routing
Java Program to Implement Bresenham Line Algorithm
Java Program to Implement Graph Structured Stack
So sánh HashSet, LinkedHashSet và TreeSet trong Java
Java Program to Implement Bit Array
Java Program to Implement Double Order Traversal of a Binary Tree