前言
以往来看,注解的属性值一般都是“硬编码”。但最近在开发过程中遇到了需要根据运行环境来设置 Retention 为 RUNTIME (运行期保留)
的注解属性值的需求。举个例子:
@Table(name="t1")
public class Test {
private String id;
...
}
对于上述类 Test
,其上有一个注解 @Table
,需求是这样的: 在测试环境 name 值为"t1",在其他环境 name 值为"t2"。
实现方法
知识点:
- 保留策略为
RUNTIME
的注解在运行期是保留的。 - 出于某些技术原因,Java 虚拟机使用的“真实”注释类的实例是动态代理的实例。
- Java 注解有一个名为
memberValues
的私有Map,其中存储了属性名称和属性值的k-v
对。
基于上述知识点,可以通过反射来访问实例,然后用给定的新值替换现有值。
相关的类:
-
Proxy JDK 动态代理大佬
可通过其getInvocationHandler
方法获取注解的代理实例 -
InvocationHandler 调用处理器,每一个被代理的实例都有一个调用处理器
通过反射获取被代理类的实例的属性值
示例代码:
// 根据运行环境获取表名
String tableName = getTable();
// 获取 Test 上的注解
Table annoTable = Test.class.getAnnotation(Table.class);
if (annoTable == null) {
throw new RuntimeException("please add @Table for Test");
}
// 获取代理处理器
InvocationHandler invocationHandler = Proxy.getInvocationHandler(annoTable);
// 过去私有 memberValues 属性
Field f = invocationHandler.getClass().getDeclaredField("memberValues");
f.setAccessible(true);
// 获取实例的属性map
Map<String, Object> memberValues = (Map<String, Object>) f.get(invocationHandler);
// 修改属性值
memberValues.put("name", tableName);