Java 反射——动态修改注解的值

反射动态修改注解的值

注解:

java 复制代码
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Value {
    String name();
}

pojo:

java 复制代码
public class User{
    
    @Value(name="张三")
    private String name;
}

现在通过注解动态将张三修改成李四,因为注解的值是在定义时就确定好的,想要修改就得通过反射进行修改,同时也可以使用外置的配置文件进行读取,类似于 Yaml 中的 @Value("${name.val}"),实际原理也是利用反射。

直接对注解进行修改:(仅展示字段上的注解)

java 复制代码
@Test
@SuppressWarnings("unchecked")
public void testModifyingAnnotations() throws Exception {
    Class<?> clazz = User.class;
    Field statType = clazz.getDeclaredField("name");
    
    Value annotation = statType.getAnnotation(Value.class);
    
    // 获取该注解,使用动态代理的方式实现修改注解的 name 值
    InvocationHandler handler = Proxy.getInvocationHandler(annotation);
    
    Field field = handler.getClass().getDeclaredField("memberValues");
    
    field.setAccessible(true);
    Map<String,Object> o = (Map<String, Object>) field.get(handler);
    
    System.out.println("原字段的注解值:"+o.get("name"));
    o.put("name","李四");
    System.out.println("修改后的注解值:"+o.get("name"));

}

结果:

text 复制代码
原字段的注解值:张三
修改后的注解值:李四

原理浅析:

可以看到我们需要修改注解的值,前提时咱们调用了 Proxy.getInvocationHandler(annotation),为什么需要用到 JDK 的代理呢?因为通过 Debug 发现 ExcelField annotation = statType.getAnnotation(ExcelField.class);

获取的注解对象 annotation,就是一个代理对象,所以我们可以通过代理对象去操作 annotation,通过观察注解对应的 InvocationHandlerAnnotationInvocationHandler 这个类。

注解的代理类有两个属性,这个很容易看出来:type 就是该注解的 Class 类型,而memberValues 就是咱们注解所存储的值了。

可以通过操作 memberValues 去操作这个注解中的值。

利用 annotation 代理对象操作注解的值

既然 AnnotationInvocationHandler 对象已经给出了注解值存储的属性,那么获取到这个属性即可。

首先获取到注解的代理对象管理器:

java 复制代码
InvocationHandler handler = Proxy.getInvocationHandler(annotation);

然后通过实际代理对象获取字段对象:memberValues

java 复制代码
Field field = handler.getClass().getDeclaredField("memberValues");

获取到属性对象:

java 复制代码
field.setAccessible(true);
Map<String, Object> memberValues = (Map<String, Object>) field.get(handler);

最后对 memberValues 进行操作:K => 注解的属性名, V => 该属性的值

java 复制代码
memberValues.set("name","李四");

到此注解的值修改完成。

总结

针对这个反射机制,有很大的操作空间再加上代理模式,我们可以在运行时进行更多的操作。

例如,现在需要根据用户的操作动态选择导出表的字段,面对这个需求,常规的 Excel 表操作肯定是不行的,所以我们可以用上反射机制:

  1. 用户根据实体类能导出的字段进行选择,然后通过 JSON,格式传递给后端
  2. 后端通过递归的方式查询和对比原有类的字段以及用户选择的字段,令用户没有选择的字段的注解值加上特殊的标识
  3. 在构建表方法中,针对注解值的特殊标识进行处理,不加入表中即可。
  4. 在导出后,最后做实体类字段注解的还原,以便用户下一次选择(因为修改后 Class 不会因为这次的方法结束而重置,直到程序重启)。

总之根据实际需求进行操作,但是一般情况下不推荐使用反射去直接修改 Class,因为操作不好,可能会导致程序出现错误,甚至安全问题。

相关推荐
该用户已不存在1 分钟前
macOS是开发的终极进化版吗?
前端·后端
计算机毕业设计木哥23 分钟前
计算机毕设选题:基于Python+Django的B站数据分析系统的设计与实现【源码+文档+调试】
java·开发语言·后端·python·spark·django·课程设计
歪歪10038 分钟前
qt creator新手入门以及结合sql server数据库开发
c语言·开发语言·后端·qt·数据库开发
布列瑟农的星空40 分钟前
大话设计模式——观察者模式和发布/订阅模式的区别
前端·后端·架构
Moonbit1 小时前
月报Vol.03: 新增Bitstring pattern支持,构造器模式匹配增强
后端·算法·github
中等生1 小时前
Pandas 与 NumPy:数据分析中的黄金搭档
后端·python
用户8356290780511 小时前
Python查找替换PDF文字:告别手动,拥抱自动化
后端·python
小猪乔治爱打球1 小时前
[Golang 修仙之路] 分布式专题:分布式锁
后端·面试
似水流年流不尽思念1 小时前
LBCC和MVCC的区别和优缺点
后端
愿你天黑有灯下雨有伞1 小时前
一种基于注解与AOP的Spring Boot接口限流防刷方案
java·spring boot·后端