1. 概述

注解(Annotation)是Java代码中的一种元数据形式,可以在编译时处理并嵌入到类文件中,也可以通过反射在运行时访问。本文将探讨如何利用反射在运行时修改注解值,并以类级注解为例进行演示。

2. 注解基础

Java允许基于现有注解创建自定义注解。最简单的注解形式是@符号后跟注解名:

@Override

让我们创建一个自定义注解Greeter

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

现在创建使用该注解的Greetings类:

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

通过反射访问注解值:

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

3. 修改注解值

Java的Class类通过一个Map管理注解——以Annotation类为键,Annotation对象为值:

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

我们将通过修改这个Map在运行时改变注解值。不同JDK实现访问方式不同,下面分别讨论JDK7和JDK8的实现。

3.1. JDK 7 实现方案

Class类中有一个annotations私有字段。需通过反射设置访问权限:

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

获取Greeter类的注解Map:

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

直接修改Map中Greeter注解的值:

map.put(targetAnnotation, targetValue);

3.2. JDK 8 实现方案

Java 8将注解信息存储在AnnotationData类中。通过反射访问私有方法annotationData

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

获取annotations字段(同样是私有字段):

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

修改注解缓存Map:

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

⚠️ 注意:JDK 8的实现比JDK 7多了一层封装,需要先获取AnnotationData对象。

4. 实战应用

看这个示例:

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

输出将是"Good morning"——因为这是我们设置的初始值。

现在创建新的Greeter对象,值为"Good evening":

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

更新注解Map:

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

再次检查注解值:

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

输出将变为"Good evening"。

✅ 验证步骤:

  1. 初始值:Good morning
  2. 修改后:Good evening
  3. 二次获取:确认值已更新

5. 总结

Java实现使用两个字段存储注解数据:

  • annotations:包含父类注解
  • declaredAnnotations:仅当前类注解

由于JDK 7和JDK 8的getAnnotation实现不同,本文为简化起见统一使用annotations字段Map进行修改。

完整实现代码可在 GitHub仓库 获取。


原始标题:Changing Annotation Parameters at Runtime Baeldung