0%

Java反射与动态代理

前言

由于最近学习框架Spring学习到了Spring的IOC容器通过反射创建Bean对象,以及AOP的动态代理也会通过反射机制动态的创建对象以及调用被增强的方法method.invoke(),这篇博客用来总结Java反射与代理模式以及对反射机制与动态代理的理解。

Java反射机制

在程序运行时,可以获取任意一个类的所有属性和方法,反射机制就是可以动态获取类的信息、访问对象属性以及调用对象的方法。

为什么要有反射机制

我们常通过反射来创建类对象,例如Spring中的Bean对象就是通过Spring的IOC容器通过反射创建对象后放到容器里供我们使用,同样都是创建对象,为什么不用new的方式创建呢?new创建对象的方式会造成程序间的耦合,在实际开发中我们应该做到在编译期不依赖,运行时才依赖,所以,我们通过反射来创建对象对程序进行解耦。

怎么通过反射创建对象

我们要创建对象需要知道类相关的信息,这些信息在Class对象中,我们要得到这些信息需要依靠类的源文件通过javac编译后产生的.class文件,.class文件保存了Class类对象,Class类对象将类的属性、方法等信息告知运行的程序。

获取Class对象:

1.我们知道具体类的情况下使用.class,不会进行类的初始化工作。

1
Class userClass = User.class;

2.不知道具体类通过Class.forName()传入类的路径获取,进行类的初始化操作(静态代码块、非静态代码块、构造函数、静态变量)

1
Class userClass = Class.forName("com.twentytwoguns.User");

创建了对象后,我们就可以对类的成员变量和成员方法进行访问。java.lang.reflect提供了Field、Method、Constructor三个类,便于我们创建对象以及访问类的成员。

Constructor:可以使用Constructor的newInstance()方法创建新的对象。
Field:可以使用set()和get()方法对成员变量进行修改和读取。
Method:可以使用invoke()方法对成员方法进行调用。

反射机制的什么时候用?

在运行时再动态加载类,通过以上方法创建对象我们就可以实现程序的间的解耦,提高了代码的灵活度,这是反射机制的优势,但是我们不能滥用反射机制,原因如下:
1.内部暴露:在正常情况下,我们是不希望调用类的私有成员,但反射通过setAccessible()可以允许我们访问私有成员。
2.性能受限:反射涉及了动态类型的解析,它通知JVM要做的事情,所以JVM无法对这些代码进行优化,性能要比非反射的操作差很多。

所以在我们使用反射机制时,要谨慎。反射的应用场景如下:
1.Class.forName()加载数据库驱动。
2.使用动态代理时会用到反射。
3.Spring中的IOC容器通过反射创建对象,AOP通过动态代理对方法进行增强时,会使用invoke()反射调用类的成员方法。

反射示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ReflectTest {
private int privateField;

public ReflectTest(){}

public ReflectTest(int privateField) {
this.privateField = privateField;
}

private void setPrivateField(int privateField) {
this.privateField = privateField;
System.out.println("This is privateMethod");
}

public void getPrivateField() {
System.out.println("privateField:" + privateField);
System.out.println("This is publicMethod");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Test{
public static void main(String[] args) throws Exception {
//获取Class对象
Class reflectTestClass = Class.forName("com.twentytwoguns.ReflectTest");
//实例化对象
ReflectTest reflectTest = (ReflectTest) reflectTestClass.getDeclaredConstructor().newInstance();
//访问私有属性, 使用Field
Field field = reflectTestClass.getDeclaredField("privateField");
//访问私有方法,使用Method
Method privateMethod = reflectTestClass.getDeclaredMethod("setPrivateField", int.class);
privateMethod.setAccessible(true);
privateMethod.invoke(reflectTest, 10);
//访问公有方法,使用Method
Method publicMethod = reflectTestClass.getDeclaredMethod("getPrivateField");
publicMethod.invoke(reflectTest);
}
}

运行结果:

1
2
3
This is privateMethod
privateField:10
This is publicMethod

说完反射机制,我们再来了解一下代理模式,以及反射在动态代理中是如何使用的。