azraelxuemo's Studio.

java动态代理

2023/11/24

什么是动态代理

静态代理和动态代理是实现代理模式的两种方式。
静态代理是在编译时就已经确定了代理类和被代理类的关系,代理类需要实现和被代理类相同的接口,并在代理类中手动编写被代理类的相关逻辑。这种方式的缺点是如果接口方法较多,或者被代理类经常发生变化,那么代理类需要频繁地进行修改,不利于维护。
动态代理则是在运行时动态生成代理类,不需要手动编写代理类的代码。在Java中,动态代理是通过反射机制实现的,通过Proxy类和InvocationHandler接口来动态生成代理对象。这种方式的优点是更加灵活,可以在运行时根据需要动态地生成代理类,而且只需要编写一次通用的代理逻辑即可。
总的来说,静态代理的代理关系在编译时就已经确定,而动态代理的代理关系在运行时动态生成,动态代理更加灵活但可能有更高的性能开销。

demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyHandler implements InvocationHandler{
private Object proxyObject;

public MyHandler(Object proxy) {
this.proxyObject=proxy;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(proxyObject,args);
System.out.println("动态代理");
return null;
}
}

在invoke里面可以定义动态代理的代码逻辑,主体还是利用反射进行调用
定义一个接口和一个简单的实现类

1
2
3
4
public interface Student {
public void study();
}

1
2
3
4
5
6
7
8
public class Boy implements Student{

@Override
public void study() {
System.out.println("学习");
}
}

使用Proxy.newProxyInstance生成动态代理对象,并进行调用测试

1
2
3
4
5
Boy xiaoMing = new Boy();
MyHandler myHandler = new MyHandler(xiaoMing);
//Student o = (Student) Proxy.newProxyInstance(xiaoMing.getClass().getClassLoader(), new Class[]{Student.class}, myHandler);
Student o = (Student) Proxy.newProxyInstance(xiaoMing.getClass().getClassLoader(), xiaoMing.getClass().getInterfaces(), myHandler);
o.study();

原理

proxyClassFile就是最后生成动态代理的class字节码,我们可以dump下来
截屏2023-11-23 17.50.40.png
构造函数直接调用Proxy的
截屏2023-11-23 17.51.49.png
var1是我们传入的handle
截屏2023-11-23 17.52.17.png
相当于把Proxy里面的这个变量赋值成我们的handle
截屏2023-11-23 17.52.55.png
有一些静态代码块,后面解释
截屏2023-11-23 17.56.32.png
发现这个动态类里面有我们的study函数,里面直接调用了handle的invoke,刚好就进入到我们自己写的invoke代码里了,这里的m3就是刚才我们静态代码反射获取的method,刚好用上了
截屏2023-11-23 17.56.54.png
同时还有一些自带的方法
截屏2023-11-24 09.54.19.png
换一个定义
截屏2023-11-23 21.02.29.png
getMethod修改了
截屏2023-11-23 21.03.19.png
方法的定义与调用也修改了
截屏2023-11-23 21.03.34.png

源码分析

ProxyGenerator.generateClassFile是生成字节码的核心部分
一上来先add这三个方法,所以可以看到我们的proxy动态类里面默认会有这三个代理实现的方法
截屏2023-11-24 09.21.09.png
遍历我们给的interface接口,然后获取Methods并加入到ProxyMethod里
截屏2023-11-24 09.21.59.png
接下来再加一个默认的构造函数,并且把之前的proxyMethods也放入到methods里面,同时还把每一个proxyMethods都对应一个field字段(m1,m2这些),同时最后还会生成一个静态代码块截屏2023-11-24 09.30.50.png
构造函数就是调用了Proxy(父类)的构造函数,传入了一个handle
截屏2023-11-24 09.29.59.png
静态代码块就是利用getMethod给我们的field字段赋值
截屏2023-11-24 09.31.50.png
上面的部分相当于是基础的信息处理,后面就是一些具体写成字节码的逻辑了,这里就不过多分析了
截屏2023-11-24 09.35.27.png

总结

其实动态代理和静态代理也比较类似
静态代理需要我们自己实现和被代理类相同的接口
而动态代理相当于jdk帮我们做了这些工作,他把需要代理的接口方法写到动态代理对象里面去了
最终这两种代理模式生成的类对象结构都类似,都重写了需要代理的接口方法
只是说静态代理把方法实现逻辑写在了代理对象里
而动态代理把实现逻辑写在了InvocationHandler的invoke里

CATALOG
  1. 1. 什么是动态代理
  2. 2. demo
  3. 3. 原理
  4. 4. 源码分析
  5. 5. 总结