Java反射&动态代理

反射机制

Java 反射(Reflection)是一种机制,它允许 Java 程序在运行时获取和操作它本身的数据结构,比如类、方法、属性等。通过反射,我们可以动态地创建对象、调用方法、访问属性等,而无需在编译时知道这些信息。

Java 反射主要包括以下几个核心类:

  1. Class 类:表示一个类或接口,包含了获取类信息的方法,如获取类名、父类、接口、属性、方法等。
  2. Constructor 类:表示一个构造函数,包含了获取构造函数信息的方法,如获取构造函数名、参数列表等。
  3. Field 类:表示一个属性,包含了获取属性信息的方法,如获取属性名、类型、访问权限等。
  4. Method 类:表示一个方法,包含了获取方法信息的方法,如获取方法名、参数列表、返回类型等。

以下是一些 Java 反射的常见用法:

  1. 获取类信息:通过 Class.forName() 方法获取某个类的 Class 对象,然后使用 Class 对象的各种方法获取类的信息,如 getName() 获取类名,getDeclaredMethods() 获取所有方法等。
  2. 创建对象:通过 Class 对象的 getConstructor()getDeclaredConstructor() 方法获取指定构造函数的引用,然后使用 newInstance()newInstance() 方法创建对象。
  3. 访问属性:通过 Class 对象的 getDeclaredField() 方法获取指定属性的引用,然后使用 set()get() 方法设置或获取属性的值。
  4. 调用方法:通过 Class 对象的 getDeclaredMethod() 方法获取指定方法的引用,然后使用 invoke() 方法调用方法并传递参数。
  5. 动态代理:通过 Java 反射实现动态代理,可以在运行时创建一个实现了指定接口的代理类,并实现指定的方法逻辑。

Class类就是通过FieldMethod来描述类中的字段和方法

代码

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
package net.Seck2y.a02;

public class Person {
private String name = "zhangsan";
private int age = 23;

public Person(){}

public Person(String name, int age) {
this.name = name;
this.age = age;
}

public void action(String string){
System.out.println(string);
}

// 重写 toString,规定打印格式,不规定会输出 类 和 地址
@Override
public String toString(){
return "Person{" +
"name: " + this.name +
", age: "+ this.age +
"}";
}
}

Reflection 类

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
package net.Seck2y.a02;

import java.lang.reflect.*;

public class Reflection {
public static void main(String[] args) throws Exception {
// Class<?>
// 获取 Person 类的 Class 对象(两种形式)
// Class<?> personClass = Person.class;
Class<?> personClass = Class.forName("net.Seck2y.a02.Person"); // 完整路径


// Constructor<?>
// 获取 Person 类的构造方法
Constructor<?> constructor = personClass.getConstructor(String.class, int.class);

// 创建 Person 对象
Object person = constructor.newInstance("lisi", 24);
System.out.println(person);
// 输出 Person{name: lisi, age: 24}


// Field
// 获取成员属性的信息(无值)
Field[] personField = personClass.getDeclaredFields();
for (Field f:personField){
System.out.println(f);
}
/* 输出
private java.lang.String net.Seck2y.a02.Person.name
private int net.Seck2y.a02.Person.age
*/

// 获取成员属性的值
Field nameField = personClass.getDeclaredField("name");
Field ageField = personClass.getDeclaredField("age");
nameField.setAccessible(true); // 提权
ageField.setAccessible(true); // 提权
String name = (String) nameField.get(person); // 获取 nameField 属性的值
int age = (int) ageField.get(person); // 获取 ageField 属性的值
System.out.println(
nameField.getName() + ": " + name + ", " +
ageField.getName() + ": " + age);
// getName 获取属性的名称
// System.out.println(person); 可替换
// 输出 name: lisi, age: 24

// 修改成员属性的值
Field setName = personClass.getDeclaredField("name");
Field setAge = personClass.getDeclaredField("age");
setName.setAccessible(true);
setAge.setAccessible(true);
setName.set(person, "wangwu");
setAge.set(person, 25);
System.out.println(
setName.getName() + ": " +setName.get(person) + ", " +
setAge.getName() + ": " + setAge.get(person));
// System.out.println(person); 可替换
// 输出 name: wangwu, age: 25


// Method
// 获取方法
Method[] personMethod = personClass.getDeclaredMethods();
for (Method m: personMethod) {
System.out.println(m);
}
/* 输出
public java.lang.String net.Seck2y.a02.Person.toString()
public void net.Seck2y.a02.Person.action(java.lang.String)
*/

// 获取 Person 对象的方法信息并调用它们
Method actionMethod = personClass.getMethod("action",String.class);
actionMethod.invoke(person,"Reflection");
// 输出 Reflection
}
}

Class

java.lang.Class类

获取对象

1
2
3
obj.getClass();
obj.class;
Class.forName(obj);

方法

1
2
3
4
5
6
7
8
9
10
11
12
getName:获取全类名
getSimpleName:获取简单类名
getFields:获取所有public修饰的属性,包含本类以及父类的
getDeclaredFields:获取本类中所有属性
getMethods:获取所有public修饰的方法,包含本类以及父类的
getDeclaredMethods:获取本类中所有方法
getConstructors:获取本类所有public修饰的构造器
getDeclaredConstructors:获取本类中所有构造器
getPackage:以Package形式返回包信息
getSuperClass:以Class形式返回父类信息
getInterfaces:以Class[]形式返回接口信息
getAnnotations:以Annotation[]形式返回注解信息

Constructor

获取无参构造方法

Java 1.9 版本中被弃用,还可以用

1
2
3
personClass.newInstance();
// 被 Constructor<?> 代替
// 使用 替换 Constructor<?> 部分
获取类的构造方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Class<?> 
// 获取 Person 类的 Class 对象
Class<?> personClass = Class.forName("net.Seck2y.a02.Person"); // 完整路径


// Constructor<?>
// 获取 Person 类的构造方法
Constructor<?> constructor = personClass.getConstructor(String.class, int.class);
// Constructor<?>[] constructor = personClass.getConstructors(); // 获取所有构造方法

// 创建 Person 对象
Object person = constructor.newInstance("lisi", 24);
// Person person = (Person) constructor.newInstance("lisi", 24);
System.out.println(person);


// 输出 无重写 toString 返回地址
net.Seck2y.a02.Person@4eec7777

Field

(public)

getField(String name) 只能获取一个指定类或其父类中的公共字段

getFields() 会返回一个包含所有公共字段的数组

(public, private, protect)

getDeclaredField(String name) 只能获取指定类中的一个字段对象

getDeclaredFields() 会返回一个包含所有字段的数组

私有成员和方法,不能直接访问

setAccessible(true) 方法是用于打破访问限制的方法,它允许访问私有字段、私有方法和私有构造函数

获取成员属性的信息(无值)
1
2
3
4
5
6
7
8
9
// 获取成员属性的信息(无值)
Field[] personField = persClass.getDeclaredFields();
for (Field f:personField){
System.out.println(f);
}

// 输出
private java.lang.String net.Seck2y.a02.Person.name
private int net.Seck2y.a02.Person.age
获取成员属性的名称和值
1
2
3
4
5
6
7
8
9
10
11
// 获取成员属性的值
Field nameField = personClass.getDeclaredField("name");
Field ageField = personClass.getDeclaredField("age");
nameField.setAccessible(true); // 提权
ageField.setAccessible(true); // 提权
String name = (String) nameField.get(person); // 获取 nameField 属性的值
int age = (int) ageField.get(person); // 获取 ageField 属性的值
System.out.println(nameField.getName() + ": " + name + ", " + ageField.getName() + ": " + age); // getName 获取属性的名称
// System.out.println(person); 可替换

// 输出 name: lisi, age: 24
修改成员属性的值
1
2
3
4
5
6
7
8
9
10
11
// 修改 Person 对象的属性信息
Field setName = personClass.getDeclaredField("name");
Field setAge = personClass.getDeclaredField("age");
setName.setAccessible(true);
setAge.setAccessible(true);
setName.set(person, "wangwu"); // 修改 setName 所获取的成员属性值
setAge.set(person, 25); // 修改 setAge 所获取的成员属性值
System.out.println(setName.getName() + ": " + setName.get(person) + ", " + setAge.getName() + ": " + setAge.get(person));
// System.out.println(person); 可替换

// 输出 name: wangwu, age: 25

Method

(public)

getMethod(String name, Class<?> ... perameterTypes) 只能获取一个指定类或其父类中的公有方法

getMethods() 会返回一个包含所有公有方法的数组

(public, private, protect)

getDeclaredMethod(String name, Class<?> ... perameterTypes) 只能获取指定类中的一个方法

getDeclaredMethods() 会返回一个包含所有方法的数组

getEnclosingMethod() 获取当前方法所属的类及方法信息,当一个方法被调用时,如果该方法不属于任何一个类的方法,或者属于一个静态方法,那么 getEnclosingMethod() 方法将返回 null

私有成员和方法,不能直接访问

setAccessible(true) 方法是用于打破访问限制的方法,它允许访问私有字段、私有方法和私有构造函数

获取方法
1
2
3
4
5
6
7
8
9
// 获取方法
Method[] personMethod = personClass.getDeclaredMethods();
for (Method m: personMethod) {
System.out.println(m);
}

// 输出
public java.lang.String net.Seck2y.a02.Person.toString()
public void net.Seck2y.a02.Person.action(java.lang.String)

getMethods可以获取包括父类的共有方法

1
2
3
4
5
6
7
8
9
10
public java.lang.String net.Seck2y.a02.Person.toString()
public void net.Seck2y.a02.Person.action(java.lang.String)
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final void java.lang.Object.wait() 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 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()
调用方法

方法 action是 public修饰,getMethod() 和 getDeclaredMethod() 都可用

1
2
3
4
// 获取 Person 对象的方法信息并调用它们
Method actionMethod = personClass.getMethod("action",String.class);
actionMethod.invoke(person,"Reflection");
// 输出 Reflection

如果方法是 private修饰

1
2
3
Method actionMethod = personClass.getDeclaredMethod("action",String.class);
actionMethod.setAccessible(true);
actionMethod.invoke(person,"Reflection");

动态代理

Java反射提供了一种类动态代理机制,可以通过代理接口实现类来完成程序无侵入式扩展

代码

Test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Test {
public static void main(String[] args) {
/*
需求:
外面的人想要大明星唱一首歌
1. 获取代理的对象
代理对象 = ProxyUtil.createProxy(大明星的对象);
2. 再调用代理的唱歌方法
代理对象.唱歌的方法("只因你太美");
*/
//1. 获取代理的对象
BigStar bigStar = new BigStar("鸡哥");
Star proxy = ProxyUtil.createProxy(bigStar);

//2. 调用唱歌的方法
String result = proxy.sing("只因你太美");
System.out.println(result);
}
}

ProxyUtil 代理

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
//  类的作用:创建一个代理
public class ProxyUtil {
/*
* 方法的作用:给一个明星的对象,创建一个代理
*
* 形参:被代理的明星对象
*
* 返回值:给明星创建的代理
*
* 需求:外面的人想要大明星唱一首歌
* 1. 获取代理的对象
* 代理对象 = ProxyUtil.createProxy(大明星的对象);
* 2. 再调用代理的唱歌方法
* 代理对象.唱歌的方法("只因你太美");
* */
public static Star createProxy(BigStar bigStar){
/* java.lang.reflect.Proxy类:提供了为对象产生代理对象的方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
参数一:用于指定用哪个类加载器,去加载生成的代理类
参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法
参数三:用来指定生成的代理对象要干什么事情
*/
Star star = (Star) Proxy.newProxyInstance(
ProxyUtil.class.getClassLoader(),//参数一:用于指定用哪个类加载器,去加载生成的代理类
new Class[]{Star.class},//参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法
//参数三:用来指定生成的代理对象要干什么事情
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*
* 参数一:代理的对象
* 参数二:要运行的方法 sing
* 参数三:调用sing方法时,传递的实参
* */
if("sing".equals(method.getName())){
System.out.println("准备话筒,收钱");
}else if("dance".equals(method.getName())){
System.out.println("准备场地,收钱");
}
//去找大明星开始唱歌或者跳舞
//代码的表现形式:调用大明星里面唱歌或者跳舞的方法
return method.invoke(bigStar,args);
}
}
);
return star;
}
}

Star 接口

1
2
3
4
5
6
7
public interface Star {
//我们可以把所有想要被代理的方法定义在接口当中
//唱歌
public abstract String sing(String name);
//跳舞
public abstract void dance();
}

BigStar

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
public class BigStar implements Star {
private String name;


public BigStar() {
}

public BigStar(String name) {
this.name = name;
}

@Override
public String sing(String name){
System.out.println(this.name + "正在唱" + name);
return "谢谢";
}

@Override
public void dance(){
System.out.println(this.name + "正在跳舞");
}

public String getName() {a
return name;
}

public void setName(String name) {
this.name = name;
}

public String toString() {
return "BigStar{name = " + name + "}";
}
}

ProxyUtil 拦截方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class ProxyUtil {
public static Star createProxy(BigStar bigStar){
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
Star star = (Star) Proxy.newProxyInstance(
ProxyUtil.class.getClassLoader(),
new Class[]{Star.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("cleanWC".equals(method.getName())){
System.out.println("拦截,不调用大明星的方法");
return null;
}
//如果是其他方法,正常执行
return method.invoke(bigStar,args);
}
}
);
return star;
}
}
⬆︎TOP