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: annotations, declaredAnnotations. 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 Program to Implement Hash Tables Chaining with Doubly Linked Lists
Remove All Occurrences of a Specific Value from a List
Java Program to Implement Binary Heap
Java Program to Describe the Representation of Graph using Adjacency Matrix
Java Program to Represent Graph Using Linked List
ExecutorService – Waiting for Threads to Finish
Java String Conversions
Java Program to Perform Insertion in a 2 Dimension K-D Tree
Jackson – Unmarshall to Collection/Array
Java Program to find the number of occurrences of a given number using Binary Search approach
Using Java Assertions
Java Program to Find the GCD and LCM of two Numbers
Logging in Spring Boot
Java Program to find the peak element of an array using Binary Search approach
Java Program to Implement Expression Tree
Convert String to Byte Array and Reverse in Java
Multipart Upload with HttpClient 4
Converting Between a List and a Set in Java
Introduction to Spring Cloud CLI
How to Read HTTP Headers in Spring REST Controllers
Working With Maps Using Streams
Java 8 Stream findFirst() vs. findAny()
Java Program to Find Whether a Path Exists Between 2 Given Nodes
Guide to Dynamic Tests in Junit 5
Java Program to Find Path Between Two Nodes in a Graph
Java Program to Implement Hopcroft Algorithm
Spring’s RequestBody and ResponseBody Annotations
Validate email address exists or not by Java Code
Hướng dẫn kết nối cơ sở dữ liệu với Java JDBC
Phương thức tham chiếu trong Java 8 – Method References
Spring Boot - Build Systems
Spring Boot - Introduction