Java反射和注解

反射

反射是指在运行的状态,对于任意一个类,都能够知道类里面的所有的属性和方法,并能够进行属性的赋值和方法的调用 。 在Java中使用Java.lang下面的Class来表示类型的”类” ,在JDK中定义接口如下

Java反射

其中T 表示运行时类的类型,如果不知道类型可以使用Class<?>,Class表示的实例表示正在运行的 Java 应用程序中的类(包含枚举) 和接口 , 所有的反射出来的结果都共享一个基类Class。

获得类型、方法、属性和构造器

在Java中有三种方法可以反射定制类的Class(以String类型为例):

1
2
3
4
5
6
1. 通过Class.from("Java.lang.String")    

2. 通过String.class

3. 通过类的实例对像中的getClass()方法 :"abc".getClass()

为了演示反射的功能,我们首先定义一个类型:

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
class Person
{
private String userName;
private String userCode;
public String Sex;//字段

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public String getUserCode() {
return userCode;
}

public void setUserCode(String userCode) {
this.userCode = userCode;
}

public String GetUserNameWithUserCode(){
return this.getUserName()+"_"+getUserCode();
}

public String GetUserNameWithUserCode(String prefix){
return prefix+"_"+ this.getUserName()+"_"+getUserCode();
}

public static String GetClassName(String prefix){
return prefix+"_Person";
}
}

获得Person类中的方法、属性和构造器,代码如下:

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
try {
Class<?> clazz = Class.forName("Person");

System.out.println("--------------Person的方法如下----------------");
Method[] methods = clazz.getMethods();
for (Method m : methods) {
System.out.println(m);
}


System.out.println("--------------Person字段如下----------------");
Field[] fields = clazz.getFields();
for (Field f : fields) {
System.out.println(f);
}

System.out.println("--------------Person构造函数如下----------------");
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
for (Constructor c : constructors) {
System.out.println(c);
}
} catch (Exception e) {
e.printStackTrace();
}
```

运行结果:

```cte
--------------Person的方法如下----------------
public Java.lang.String Person.getUserName()
public Java.lang.String Person.GetUserNameWithUserCode()
public Java.lang.String Person.GetUserNameWithUserCode(Java.lang.String)
public void Person.setUserName(Java.lang.String)
public Java.lang.String Person.getUserCode()
public void Person.setUserCode(Java.lang.String)
public static Java.lang.String Person.GetClassName(Java.lang.String)
public final void Java.lang.Object.wait() throws Java.lang.InterruptedException
public final void Java.lang.Object.wait(long,int) throws Java.lang.InterruptedException
public final native void Java.lang.Object.wait(long) throws Java.lang.InterruptedException
public boolean Java.lang.Object.equals(Java.lang.Object)
public Java.lang.String Java.lang.Object.toString()
public native int Java.lang.Object.hashCode()
public final native Java.lang.Class Java.lang.Object.getClass()
public final native void Java.lang.Object.notify()
public final native void Java.lang.Object.notifyAll()
--------------Person字段如下----------------
public Java.lang.String Person.Sex
--------------Person构造函数如下----------------
Person()

```

#### 方法的调用和属性的赋值

上面的代码我们能够获得对应的属性和方法,下面就解决如何去调用当前方法和属性进行赋值:

```Java
try {
Class clazz =Class.forName("Person");
Object obj=clazz.newInstance();//创建对象

//set方法的获得
Method setUserName=clazz.getMethod("setUserName",String.class);
Method setUserCode=clazz.getMethod("setUserCode",String.class);
//set方法的调用
setUserName.invoke(obj,"fuwei");
setUserCode.invoke(obj,"F0001");

//一般方法的获得和调用
Method m1=clazz.getMethod("GetUserNameWithUserCode");
String s1= m1.invoke(obj,null).toString();

Method m2=clazz.getMethod("GetUserNameWithUserCode",String.class);
String s2= m2.invoke(obj,new Object[]{"Test"}).toString();

//静态方法的调用
Method m3=clazz.getMethod("GetClassName",String.class);
String s3= m3.invoke(null,new Object[]{"Test"}).toString();

System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
}
catch (Exception e) {
e.printStackTrace();
}

```

输出结果:

```cte
fuwei_F0001
Test_fuwei_F0001
Test_Person

使用反射实现动态代理

为了更好的理解动态代理,首先要理解静态代理的逻辑,具体的实现示意图:

动态代理

具体实现类代码如下:

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
interface ProxyInterface {
public void doSomething();
}

//被代理者
class RealObj implements ProxyInterface {
@Override
public void doSomething() {
System.out.println("doSomething");
}

}

//代理者
class ProxyObj implements ProxyInterface {
@Override
public void doSomething() {
System.out.println("before Exector");
new RealObj().doSomething();
System.out.println("after Exector");
}

}


对应的调用代码:

1
2
3
4
5
6
7
8
9
10
11

@Test
public void TestProxy(){
CallMethod(new ProxyObj());

}

public void CallMethod(ProxyInterface pi){
pi.doSomething();
}

运行结果:

1
2
3
4
before Exector
doSomething
after Exector

在静态的代理中,可以看到是在代理类的内部使用了真实类的实例,来实现代理的功能,但这样就只能代理指定的类的类型,丧失了灵活性。如果我们能够在代理的时候把数据传入到代理类中,就可以动态的实现类的代理。 代码实现原理如下:

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
interface ProxyInterface {
public void doSomething();
}

//被代理者
class RealObj implements ProxyInterface {
@Override
public void doSomething() {
System.out.println("doSomething");
}

}

//代理者
class ProxyObj implements InvocationHandler {

private Object subject;
public ProxyObj(Object subject)
{
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

System.out.println("Before Exector");
Object obj = method.invoke(subject, args);
System.out.println("After Exector");

return obj;
}
}

调用的实现:

1
2
3
4
5
6
7
8
9
10
11

public void TestProxy()
{
RealObj real = new RealObj();
ProxyInterface proxy = (ProxyInterface) Proxy.newProxyInstance(
ProxyInterface.class.getClassLoader(),
new Class[]{ProxyInterface.class},
new ProxyObj(real));
proxy.doSomething();
}

对于这个实现的原理可以参考这篇博客细说JDK动态代理的实现原理 {:target=_blank}, 生成了一个继承自Proxy和实现了ProxyInterface方法的一个对象,具体的示意为:

1
2
3
4
class proxy extends Proxy implements HelloWorld{
....
}

注解

Java注解提供了关于代码的一些信息,但并不直接作用于它所注解的代码内容,常用的注解有可以参考:Java注释Override、Deprecated、SuppressWarnings详解

自定义注解

注解的大多使用情况都是结合反射,在Spring框架中也有很多都是使用反射+注解的方法来实现,下面为了更深入了解注解,我们可以自定义一个注解,注解在Java中的实现很简单:

1
2
3
4
5
6
public @interface MyAnno   
{

}


只需要这样定义就可以直接使用这个注解 ,但这个是没有任何的实际意义。在这个注解中,可以添加对应的内部方法:

1
2
3
4
5
6
7
8

public @interface MyAnno
{

String value();
}


在使用的时候,我们就可以对value进行赋值:

1
2
3
4
5
6
@MyAnno(value = "test")
public void TestReflect()
{

}

对于上面的代码,注解没有干涉内部代码的逻辑,但也没有起任何的作用。对于注解的使用,我们可以根据上面说到反射来实现我们的目的。比如,我们想实现一个注解为test的方法调用,可以先获得一个方法的所有注解,然后根据注解的value值来判断是否调用 ,下面是获得所有注解的方法:

1
2
3
4
5
6
7
8
9
10

Method[] methods = clazz.getMethods();
for (Method m : methods) {
Annotation[] ans= m.getDeclaredAnnotations();
for (Annotation an : ans)
{
System.out.println(an);
}
}

作者

付威

发布于

2017-09-17

更新于

2020-08-10

许可协议

评论