- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Java通过反射,如何动态修改注解的某个属性值由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
昨晚看到一条问题,大意是楼主希望可以动态得建立多个Spring 的定时任务.
这个题目我并不是很熟悉,不过根据题目描述和查阅相关Spring 创建定时任务的资料,发现这也许涉及到通过Java代码动态修改注解的属性值.
今天对此尝试了一番, 。
众所周知,java/lang/reflect这个包下面都是Java的反射类和工具.
Annotation注解,也是位于这个包里的。注解自从Java 5.0版本引入后,就成为了Java平台中非常重要的一部分,常见的如@Override、@Deprecated.
关于注解更详细的信息和使用方法,网上已经有很多资料,这里就不再赘述了.
一个注解通过@Retention指定其生命周期,本文所讨论的动态修改注解属性值,建立在@Retention(RetentionPolicy.RUNTIM)这种情况。毕竟这种注解才能在运行时(runtime)通过反射机制进行操作.
那么现在我们定义一个@Foo注解,它有一个类型为String的value属性,该注解应用再Field上
1
2
3
4
5
6
7
8
|
/**
* Created by krun on 2017/9/18.
*/
@Target
(ElementType.FIELD)
@Retention
(RetentionPolicy.RUNTIME)
public
@interface
Foo {
String value();
}
|
再定义一个普通的Java对象Bar,它有一个私有的String属性val,并为它设置属性值为"fff"的@Foo注解
1
2
3
4
5
|
public
class
Bar {
@Foo
(
"fff"
)
private
String val;
}
|
接下来在main方法中我们来尝试修改Bar.val上的@Foo注解的属性值为"ddd".
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/**
* Created by krun on 2017/9/18.
*/
public
class
Main {
public
static
void
main(String ...args)
throws
NoSuchFieldException {
//获取Bar实例
Bar bar =
new
Bar();
//获取Bar的val字段
Field field = Bar.
class
.getDeclaredField(
"val"
);
//获取val字段上的Foo注解实例
Foo foo = field.getAnnotation(Foo.
class
);
//获取Foo注解实例的 value 属性值
String value = foo.value();
//打印该值
System.out.println(value);
// fff
}
}
|
首先,我们要知道注解的值是存在哪里的.
在String value = foo.value();处下断点,我们跑一下可以发现:
当前栈中有这么几个变量,不过其中有一点很特别:foo,其实是个Proxy实例.
Proxy也是java/lang/reflect下的东西,它的作用是为一个Java类生成一个代理,就像这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
public
interface
A {
String func1();
}
public
class
B
implements
A {
@Override
public
String func1() {
//do something ... }
public
String func2() {
//do something ... };
}
public
static
void
main(String ...args) {
B bInstance =
new
B();
B bProxy = Proxy.newProxyInstance(
B.
class
.getClassLoader(),
// B 类的类加载器
B.
class
.getInterfaces(),
// B 类所实现的接口,如果你想拦截B类的某个方法,必须让这个方法在某个接口中声明并让B类实现该接口
new
InvocationHandler() {
// 调用处理器,任何对 B类所实现的接口方法的调用都会触发此处理器
@Override
public
Object invoke (Object proxy,
// 这个是代理的实例,method.invoke时不能使用这个,否则会死循环
Method method,
// 触发的接口方法
Object[] args
// 此次调用该方法的参数
)
throws
Throwable {
System.out.println(String.format(
"调用 %s 之前"
, method.getName()));
/**
* 这里必须使用B类的某个具体实现类的实例,因为触发时这里的method只是一个接口方法的引用,
* 也就是说它是空的,你需要为它指定具有逻辑的上下文(bInstance)。
*/
Object obj = method.invoke(bInstance, args);
System.out.println(String.format(
"调用 %s 之后"
, method.getName()));
return
obj;
//返回调用结果
}
}
);
}
|
这样你就可以拦截这个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
2
3
4
5
6
|
class
AnnotationInvocationHandler
implements
InvocationHandler, Serializable {
private
final
Class<?
extends
Annotation> type;
private
final
Map<String, Object> memberValues;
private
transient
volatile
Method[] memberMethods =
null
;
/* 后续无关代码就省略了,想看的话可以查看 sun/reflect/annotation/AnnotationInvocationHandler */
}
|
我们一眼就可以看到一个有意思的名字:memberValues,这是一个Map,而断点中可以看到这是一个LinknedHashMap,key为注解的属性名称,value即为注解的属性值.
现在我们找到了注解的属性值存在哪里了,那么接下来的事就好办了:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
/**
* Created by krun on 2017/9/18.
*/
public
class
Main {
public
static
void
main(String ...args)
throws
NoSuchFieldException, IllegalAccessException {
//获取Bar实例
Bar bar =
new
Bar();
//获取Bar的val字段
Field field = Bar.
class
.getDeclaredField(
"val"
);
//获取val字段上的Foo注解实例
Foo foo = field.getAnnotation(Foo.
class
);
//获取 foo 这个代理实例所持有的 InvocationHandler
InvocationHandler h = Proxy.getInvocationHandler(foo);
// 获取 AnnotationInvocationHandler 的 memberValues 字段
Field hField = h.getClass().getDeclaredField(
"memberValues"
);
// 因为这个字段事 private final 修饰,所以要打开权限
hField.setAccessible(
true
);
// 获取 memberValues
Map memberValues = (Map) hField.get(h);
// 修改 value 属性值
memberValues.put(
"value"
,
"ddd"
);
// 获取 foo 的 value 属性值
String value = foo.value();
System.out.println(value);
// ddd
}
}
|
java/lang/reflect 这个包下面都是Java的反射类和工具.
Annotation 注解,也是位于这个包里的.
注解自从Java 5.0版本引入后,就成为了Java平台中非常重要的一部分,常见的有 @Override、 @Deprecated 。
关于注解更详细的信息和使用方法,网上已经有很多资料,自行查看.
一个注解通过 @Retention 指定其生命周期,本文所讨论的动态修改注解属性值,建立在 @Retention(RetentionPolicy.RUNTIM) 这种情况.
这种注解才能在运行时(runtime)通过反射机制进行修改属性的操作.
它有一个类型为 String 的 name属性,该注解应用再Method上
1
2
3
4
5
6
|
@Target
({ ElementType.METHOD })
@Retention
(RetentionPolicy.RUNTIME)
@Component
public
@interface
TestAnno {
String name()
default
""
;
}
|
我用自定义注解首先得了解清楚注解的值存储在什么地方,我们可以写个main方法测试一下:
我们定义了一个RetryTestService 在它的方法 retryTest() 上添加@TestAnno 注解,然后在main方法里面反射获取注解的name值 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
@Service
public
class
RetryTestService {
@TimeLog
@TestAnno
(name =
"${nba.kobe}"
)
public
String retryTest(){
System.out.println(
"---进行了接口请求...."
);
return
"success"
;
}
public
static
void
main(String[] args)
throws
NoSuchMethodException {
RetryTestService service =
new
RetryTestService();
Method method = service.getClass().getDeclaredMethod(
"retryTest"
,
null
);
TestAnno testAnno = method.getDeclaredAnnotation(TestAnno.
class
);
System.out.println(testAnno.name());
}
}
|
当前栈中有这么几个变量,不过其中有一点很特别:@TestAnno,其实是个Proxy实例.
Proxy也是 java/lang/reflect下的东西,它的作用是为一个Java类生成一个代理,就像这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
public
interface
A {
String func1();
}
public
class
B
implements
A {
@Override
public
String func1() {
//do something ... }
public
String func2() {
//do something ... };
}
public
static
void
main(String ...args) {
B bInstance =
new
B();
B bProxy = Proxy.newProxyInstance(
B.
class
.getClassLoader(),
// B 类的类加载器
B.
class
.getInterfaces(),
// B 类所实现的接口,如果你想拦截B类的某个方法,必须让这个方法在某个接口中声明并让B类实现该接口
new
InvocationHandler() {
// 调用处理器,任何对 B类所实现的接口方法的调用都会触发此处理器
@Override
public
Object invoke (Object proxy,
// 这个是代理的实例,method.invoke时不能使用这个,否则会死循环
Method method,
// 触发的接口方法
Object[] args
// 此次调用该方法的参数
)
throws
Throwable {
System.out.println(String.format(
"调用 %s 之前"
, method.getName()));
/**
* 这里必须使用B类的某个具体实现类的实例,因为触发时这里的method只是一个接口方法的引用,
* 也就是说它是空的,你需要为它指定具有逻辑的上下文(bInstance)。
*/
Object obj = method.invoke(bInstance, args);
System.out.println(String.format(
"调用 %s 之后"
, method.getName()));
return
obj;
//返回调用结果
}
}
);
}
|
注意了:
ClassLoader 这是个class就会有,注解也不例外。那么注解和interfaces有什么关系?
注解本质上就是一个接口,它的实质定义为: interface SomeAnnotation extends Annotation.
这个 Annotation 接口位于 java/lang/annotation 包,它的注释中第一句话就是 The common interface extended by all annotation types. 。
如此说来,@TestAnno 注解本身只是个接口,这就意味着它没有任何代码逻辑,那么它的 value 属性究竟是存在哪里的呢?
这个 Proxy 实例持有一个 AnnotationInvocationHandler,还记得之前提到过如何创建一个 Proxy 实例么? 第三个参数就是一个 InvocationHandler.
1
2
3
4
5
6
7
8
|
class
AnnotationInvocationHandler
implements
InvocationHandler, Serializable {
private
final
Class<?
extends
Annotation> type;
private
final
Map<String, Object> memberValues;
private
transient
volatile
Method[] memberMethods =
null
;
/* 后续无关代码就省略了,想看的话可以查看 sun/reflect/annotation/AnnotationInvocationHandler */
}
|
我们一眼就可以看到一个有意思的名字: memberValues,这是一个Map,而断点中可以看到这是一个 LinknedHashMap,key为注解的属性名称,value即为注解的属性值.
现在我们找到了注解的属性值存在哪里了,那么接下来的事就好办了:
我这里写两个aop。第一个aop拦截带@TestAnno注解的方法,然后改变注解的name值,第二个aop我们再把注解的name值打印出来,看看是不是真被改了 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
@Aspect
@Component
@Order
(
1
)
//aop执行顺序1表示先执行此aop
public
class
AuthDemoAspect
implements
EnvironmentAware {
Environment environment;
@Override
public
void
setEnvironment(Environment environment) {
this
.environment = environment;
}
@Pointcut
(
"@annotation(com.ali.hangz.tooltest.config.TestAnno)"
)
public
void
myPointCut() {
}
@Before
(value =
"myPointCut()"
)
public
void
check(){
}
@After
(value =
"myPointCut()"
)
public
void
bye(){
}
/**
*配置文件配置
* @return
*/
@Around
(
"myPointCut() && @annotation(testAnno)"
)
public
Object around(ProceedingJoinPoint joinPoint, TestAnno testAnno){
try
{
System.out.println(
"---修改前注解@TestAnno的name指为:"
+ testAnno.name());
String s = environment.resolvePlaceholders(testAnno.name());
//获取 foo 这个代理实例所持有的 InvocationHandler
InvocationHandler h = Proxy.getInvocationHandler(testAnno);
// 获取 AnnotationInvocationHandler 的 memberValues 字段
Field hField = h.getClass().getDeclaredField(
"memberValues"
);
// 因为这个字段事 private final 修饰,所以要打开权限
hField.setAccessible(
true
);
// 获取 memberValues
Map memberValues = (Map) hField.get(h);
// 修改 value 属性值
memberValues.put(
"name"
,s);
return
joinPoint.proceed();
}
catch
(Throwable throwable) {
throwable.printStackTrace();
}
return
null
;
}
}
|
第一个aop里面我改变注解的name值,由上面service方法上注解的${nba.kobe} 改成读取配置文件 nba.kobe的配置值 。
项目配置文件:application.properties增加一个值 。
1
2
|
nba.kobe=科比
String s = environment.resolvePlaceholders(testAnno.name());
|
这行代码其实就是通过原本注解值${nba.kobe}去配置文件取nba.kobe 对应的值。如果你只是修改原来注解的name值而不是去取配置文件大可以不用此行代码,直接给memberValues 里面的name put新的值就行.
注意:@Order(1) 可以控制aop的执行顺序 。
然后我再写第二个aop,打印出注解@TestAnno 的name值看看是不是第一个aop已经成功把值改掉了 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
@Aspect
@Component
@Order
(
2
)
public
class
AuthDemoAspectTwo
implements
EnvironmentAware {
Environment environment;
@Override
public
void
setEnvironment(Environment environment) {
this
.environment = environment;
}
@Pointcut
(
"@annotation(com.ali.hangz.tooltest.config.TestAnno)"
)
public
void
myPointCut() {
}
@Before
(value =
"myPointCut()"
)
public
void
check(){
}
@After
(value =
"myPointCut()"
)
public
void
bye(){
}
/**
*配置文件配置
* @return
*/
@Around
(
"myPointCut() && @annotation(testAnno)"
)
public
Object around(ProceedingJoinPoint joinPoint, TestAnno testAnno){
try
{
System.out.println(
"---修改后的注解名称:"
+ testAnno.name());
return
joinPoint.proceed();
}
catch
(Throwable throwable) {
throwable.printStackTrace();
}
return
null
;
}
|
然后我们只需要启动项目调用一下RetryTestService的 retryTest()方法 就可以进入aop 看看打印出来的结果了 。
通过结果我们可以发现第一个aop的确把retryTest()方法上面注解@TestAnno的name值由原先的 @TestAnno(name = "${nba.kobe}") ${nba.kobe}值动态修改成了配置文件里面配置的“科比”了.
以上为个人经验,希望能给大家一个参考,也希望大家多多支持我.
原文链接:https://segmentfault.com/a/1190000011213222 。
最后此篇关于Java通过反射,如何动态修改注解的某个属性值的文章就讲到这里了,如果你想了解更多关于Java通过反射,如何动态修改注解的某个属性值的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
一、反射 1.定义 Java的反射(reflection)机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法(即使是私有的);对于任意一个对象,都能够调用它的任意方法和属性,那么,我
有没有办法从 JavaScript 对象内部获取所有方法(私有(private)、特权或公共(public))?这是示例对象: var Test = function() { // private m
我有一个抽象类“A”,类“B”和“C”扩展了 A。我想在运行时根据某些变量创建这些实例。如下所示: public abstract class A { public abstract int
假设我们在内存中有很多对象。每个都有一个不同的ID。如何迭代内存以找到与某些 id 进行比较的特定对象?为了通过 getattr 获取并使用它? 最佳答案 您应该维护这些对象的集合,因为它们是在类属性
假设我有这个结构和一个方法: package main import ( "fmt" "reflect" ) type MyStruct struct { } func (a *MyS
C#反射简介 反射(Reflection)是C#语言中一种非常有用的机制,它可以在运行时动态获取对象的类型信息并且进行相应的操作。 反射是一种在.NET Framework中广
概述 反射(Reflection)机制是指在运行时动态地获取类的信息以及操作类的成员(字段、方法、构造函数等)的能力。通过反射,我们可以在编译时期未知具体类型的情况下,通过运行时的动态
先来看一段魔法吧 public class Test { private static void changeStrValue(String str, char[] value) {
结构体struct struct 用来自定义复杂数据结构,可以包含多个字段(属性),可以嵌套; go中的struct类型理解为类,可以定义方法,和函数定义有些许区别; struct类型是值类型
反射 1. 反射的定义 Java的反射(reflection)机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,既然能拿到那么,我们
反射的定义 java的反射(reflection) 机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,既然能拿到嘛,那么,我们就可以
我有一个 Java POJO: public class Event { private String id; private String name; private Lon
我编写了以下函数来检查给定的单例类是否实现了特征。 /** Given a singleton class, returns singleton object if cls implements T.
我正在研究 Java 反射的基础知识并观察有关类方法的信息。我需要获得一个符合 getMethod() 函数描述的规范的方法。然而,当我这样做时,我得到了一个 NoSuchMethodExceptio
我正在通过以下代码检索 IEnumerable 属性列表: BindingFlags bindingFlag = BindingFlags.Instance | BindingFlags.Public
我需要检查属性是否在其伙伴类中定义了特定属性: [MetadataType(typeof(Metadata))] public sealed partial class Address { p
我正在尝试使用 Reflections(由 org.reflections 提供)来处理一些繁重的工作,因此我不需要在很长的时间内为每个类手动创建一个实例列表。但是,Reflections 并未按照我
scala 反射 API (2.10) 是否提供更简单的方法来搜索加载的类并将列表过滤到实现定义特征的特定类? IE; trait Widget { def turn(): Int } class
我想在运行时使用反射来查找具有给定注释的所有类,但是我不知道如何在 Scala 中这样做。然后我想获取注释的值并动态实例化每个映射到关联注释值的带注释类的实例。 这是我想要做的: package pr
这超出了我的头脑,有人可以更好地向我解释吗? http://mathworld.wolfram.com/Reflection.html 我正在制作一个 2d 突破格斗游戏,所以我需要球能够在它击中墙壁
我是一名优秀的程序员,十分优秀!