Java动态代理
概念
代理模式是Java中常用的设计模式。
其特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。
代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法来提供简单的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。
静态代理
指在编译时接口,代理类,被代理类等就已经确定下来了,在程序运行之前,代理类的.class文件就已经生成。
需要代理类与委托类有相同的接口
缺点:当需要代理的对象过多就需要实现大量的代理类,并且当接口增加方法,目标对象与代理对象都要进行修改
Demo:
拿学生交作业举例,一般都是学生交给课代表,课代表交给老师这种模式。此时学生就相当于委托类,课代表相当于一个学生代理类。
这里需要一个公共的接口,即学生(委托类)和课代表(代理类)的公共接口:
1 | public interface Event{ |
编写一个学生类(委托类),实现公共代理接口和重写接口中的方法:
1 | public class Student implements Event{ |
可理解为这个类实现学生将作业交给课代表,即从委托类到代理类。
同时编写一个代理类,即课代表,实现从代理类到目标对象,同样需要公共代理接口和重写接口中的方法
1 | public class StudentInnovation implements Event{ |
代理:
1 | public class Main { |
1 | 张三提交作业 |
动态代理
与静态代理原理相同,需要公共接口,委托类和代理类。区别就是动态代理是利用反射机制在运行时创建代理类。
主要通过Java.lang.reflect.Proxy类与InvocationHandler接口
InvocationHandler接口:负责提供调用代理的操作
1 | public interface InvocationHandler { |
其中proxy为动态生成的代理对象(不是被代理的实际对象)
method表示调用的方法名(通过反射获取的Method对象)
args为调用方法的参数数组
Proxy类:负责动态构建代理类
该类提供了一个静态方法用于得到代理对象:
1 | public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler handler) |
loader表示类加载器(通常使用目标接口的类加载器,用于加载动态生成的代理类)
第二个参数指代理类要实现的接口列表
第三个参数指方法调用的处理器
Demo:
委托类和公共接口不用修改:
1 | public interface Event { |
1 | public class Student implements Event{ |
编写动态获取代理类的类:
1 | import java.lang.reflect.InvocationHandler; |
其中:
1 |
|
当调用代理对象的方法时就会触发invoke方法,触发该invoke方法的前提条件:
代理对象需基于接口动态生成,并绑定
InvocationHandler。1
2
3
4
5
6InvocationHandler handler = new ProxyHandler(s1);
Event proxy = (Event) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(), // 必须实现至少一个接口
handler // InvocationHandler 实例
);调用的方法必须属于代理接口
方法调用必须通过代理对象发起
这里我们设置一个动态代理:
1 | import java.lang.reflect.InvocationHandler; |
1 | 张三提交作业 |
其中调用Proxy.newProxyInstance()时,JVM首先会根据传入的interfaces接口列表,动态生成一个新的类(代理类),该类会实现所有指定的接口。然后使用传入ClassLoader类加载器加载生成的代理类字节码,将其定义为新的Java类,最后通过反射调用代理类的构造函数,传入调用处理器handler,创建代理对象。
当调用proxyHello.SubmitWork();触发invoke方法,object代理对象为proxyHello,args为null,即调用我们的proxyHello.Submitwork()
参考
https://www.cnblogs.com/gonjan-blog/p/6685611.html
https://nivi4.notion.site/Java-b97e232be04941a7860206a0e908f2cd
https://liaoxuefeng.com/books/java/reflection/proxy/index.html








