反射动态修改注解属性本质是操作annotationinvocationhandler的membervalues映射,仅适用于@retention(retentionpolicy.runtime)注解,可实现多环境差异化配置,但仅对当前jvm有效且不能新增属性。

反射动态修改注解属性,本质是绕过注解的“只读”表象,直接操作其底层代理对象的 memberValues 映射。它不改变源码,却能在运行时按需调整行为——特别适合开发、测试、预发等多环境共用一套代码,但需差异化配置的场景(比如只在 dev 环境开启懒加载、关闭超时重试、启用调试日志开关等)。
前提:注解必须声明为 RUNTIME 生命周期
只有带 @Retention(RetentionPolicy.RUNTIME) 的注解,才能在 JVM 运行期被反射读取和操作。编译期(SOURCE)或类加载期(CLASS)的注解无法触达。
例如,一个用于远程服务引用的自定义注解:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME) // ✅ 必须有这一行
public @interface RemoteService {
String group() default "";
int timeout() default 3000;
boolean lazy() default false;
}
核心原理:穿透 JDK 动态代理的 memberValues
Java 中所有运行时注解实例,实际都是由 AnnotationInvocationHandler 代理的。它的私有字段 memberValues(类型为 Map<string object></string>)真正存储着你声明的属性键值对。
所以修改注解属性 = 获取该代理对象 → 反射访问并修改其 memberValues。
关键步骤如下:
- 通过
field.getAnnotation(YourAnnotation.class)获取注解实例 - 调用
Proxy.getInvocationHandler(annotation)拿到代理处理器 - 用反射获取处理器内部的
memberValues字段,并设为可访问 - 向该
Map中put新值(如put("lazy", true))
典型应用:按 profile 自动注入开发专用配置
假设项目使用 Spring Boot,希望在 spring.profiles.active=dev 时,统一将所有 @RemoteService 注解的 lazy=true、timeout=1000,而生产环境保持默认。
可在 Spring 容器刷新前(如实现 BeanFactoryPostProcessor)执行批量修正:
public class DevAnnotationConfigurer implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
if (!"dev".equals(System.getProperty("spring.profiles.active"))) return;
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
Class> clazz = Class.forName(bd.getBeanClassName());
for (Field field : clazz.getDeclaredFields()) {
RemoteService anno = field.getAnnotation(RemoteService.class);
if (anno != null) {
InvocationHandler handler = Proxy.getInvocationHandler(anno);
Field mValues = handler.getClass().getDeclaredField("memberValues");
mValues.setAccessible(true);
Map<string object> map = (Map<string object>) mValues.get(handler);
map.put("lazy", true);
map.put("timeout", 1000);
}
}
}
}
}</string></string>
这样无需改任何业务类上的注解,也无需写大量条件判断,就能实现环境感知的配置注入。
注意事项与限制
- 修改仅对当前 JVM 实例有效,不影响字节码文件
- 不能新增属性,只能覆盖已有 key;key 名必须与注解接口中方法名完全一致(区分大小写)
- 若注解属性是枚举、数组或嵌套注解,需确保 put 的值类型匹配,否则运行时报
ClassCastException - Spring 的
@Value、@Scheduled等原生注解不可直接修改——它们不是靠反射读取注解属性驱动逻辑,而是由 Spring 自己解析并缓存结果;需在 Spring 解析前干预(如扩展AnnotationConfigUtils或自定义BeanPostProcessor)










