昨晚看到一条问题,大意是楼主希望可以动态得建立多个Spring 的定时任务。
这个题目我并不是很熟悉,不过根据题目描述和查阅相关 Spring 创建定时任务 的资料,发现这也许涉及到通过Java代码动态修改注解的属性值。
今天对此尝试了一番,发现通过反射来动态修改注解的属性值是可以做到的:
众所周知,java/lang/reflect 这个包下面都是Java的反射类和工具。
Annotation 注解,也是位于这个包里的。注解自从Java 5.0版本引入后,就成为了Java平台中非常重要的一部分,常见的如 @Override、 @Deprecated。
关于注解更详细的信息和使用方法,网上已经有很多资料,这里就不再赘述了。
一个注解通过 @Retention 指定其生命周期,本文所讨论的动态修改注解属性值,建立在 @Retention(RetentionPolicy.RUNTIM) 这种情况。毕竟这种注解才能在运行时(runtime)通过反射机制进行操作。
那么现在我们定义一个 @Foo 注解,它有一个类型为 String 的 value 属性,该注解应用再Field上:
1 | 复制代码 |
再定义一个普通的Java对象 Bar,它有一个私有的String属性 val,并为它设置属性值为"fff" 的 @Foo 注解:
1 | 复制代码 |
接下来在 main 方法中我们来尝试修改 Bar.val 上的 @Foo注解的属性值为 "ddd"。
先是正常的获取注解属性值:
1 | 复制代码 |
首先,我们要知道注解的值是存在哪里的。
在 String value = foo.value(); 处下断点,我们跑一下可以发现:
当前栈中有这么几个变量,不过其中有一点很特别:foo,其实是个Proxy实例。
Proxy也是 java/lang/reflect下的东西,它的作用是为一个Java类生成一个代理,就像这样:
1 | 复制代码 |
这样你就可以拦截这个Java类的某个方法调用,但是你只能拦截到 func1的调用,想想为什么?
那么注意了:
ClassLoader 这是个class就会有,注解也不例外。那么注解和interfaces有什么关系?
注解本质上就是一个接口,它的实质定义为: interface SomeAnnotation extends Annotation。
这个 Annotation 接口位于 java/lang/annotation 包,它的注释中第一句话就是 The common interface extended by all annotation types.
如此说来,Foo 注解本身只是个接口,这就意味着它没有任何代码逻辑,那么它的 value 属性究竟是存在哪里的呢?
展开 foo 可以发现:
这个 Proxy 实例持有一个 AnnotationInvocationHandler,还记得之前提到过如何创建一个 Proxy 实例么? 第三个参数就是一个 InvocationHandler。
看名字这个handler即是Annotation所特有的,我们看一下它的代码:
1 | 复制代码 |
我们一眼就可以看到一个有意思的名字: memberValues,这是一个Map,而断点中可以看到这是一个 LinknedHashMap,key为注解的属性名称,value即为注解的属性值。
现在我们找到了注解的属性值存在哪里了,那么接下来的事就好办了:
1 | 复制代码/** |
本文转载自: 掘金