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"。
✅ 验证步骤:
- 初始值:Good morning
- 修改后:Good evening
- 二次获取:确认值已更新
5. 总结
Java实现使用两个字段存储注解数据:
annotations
:包含父类注解declaredAnnotations
:仅当前类注解
由于JDK 7和JDK 8的getAnnotation
实现不同,本文为简化起见统一使用annotations
字段Map进行修改。
完整实现代码可在 GitHub仓库 获取。