这篇文章主要介绍了Java四种动态代理实现方式,对于开始学习java动态代理或者要复习java动态代理的朋友来讲很有参考价值,有感兴趣的朋友可以参考一下
代理模式也是一种非常常见的设计模式。了解Spring框架的都知道,Spring AOP 使用的就是动态代理模式。今天就来系统的重温一遍代理模式。
在现实生活中代理是随处可见的,当事人因某些隐私不方便出面,或者当事人不具备某些相关的专业技能,而需要一个职业人员来完成一些专业的操作, 也可能由于当事人没有时间处理事务,而聘用代理人出面。而在软件设计中,使用代理模式的地方也很多,由于安全原因,屏蔽客户端直接访问真实对象, 或者为了提升系统性能,使用代理模式实现延迟加载,还有就是AOP,对委托类的功能进行增强等。
一、代理模式的结构
代理模式的主要参与者有4个,如下图所示:
角色 | 作用 |
---|---|
Subject | 主题接口,定义了代理类和委托类的公共对外方法,也是代理类代理委托类的方法 |
RealSubject | 委托类,真实主题,真正实现业务逻辑的类 |
Proxy | 代理类,代理和封装委托类 |
Client | 客户端,使用代理类和主题接口完成业务逻辑 |
loading="lazy" alt="" />角色作用Subject主题接口,定义了代理类和委托类的公共对外方法,也是代理类代理委托类的方法RealSubject委托类,真实主题,真正实现业务逻辑的类Proxy代理类,代理和封装委托类Client客户端,使用代理类和主题接口完成业务逻辑
二、代理模式的实现
代理模式一般分为静态代理和动态代理两种:
- 静态代理,顾名思义,就是提前创建好代理类文件并在程序运行前已经编译成字节码。
- 动态代理,是指在运行时动态生成代理类,即代理类的字节码将在运行时生成并载入到ClassLoader中。
了解了两种代理模式大概区别后,接下来就以一个短信发送功能增强的示例来详细阐述两种代理的实现方式。
1、静态代理实现
第一步,定义主题接口,该接口只有一个send
方法:
public interface ISender { public boolean send(); }
第二步,定义主题真正实现类:
public class SmsSender implements ISender { public boolean send() { System.out.println("sending msg"); return true; } }
第三步,创建代理类,封装实现类:
public class ProxySender implements ISender { private ISender sender; public ProxySender(ISender sender){ this.sender = sender; } public boolean send() { System.out.println("处理前"); boolean result = sender.send(); System.out.println("处理后"); return result; } }
第四步,客户端调用:
@Test public void testStaticProxy(){ ISender sender = new ProxySender(new SmsSender()); boolean result = sender.send(); System.out.println("输出结果:" + result); }
以上就实现了一个简单的静态代理,很明显,静态代理需要为每个真实主题定义一个形式上完全一样的封装类,
如果真实主题方法有所修改,那代理类也需要跟着修改,不利于系统的维护。
2、动态代理实现
与静态代理相比,动态代理有更多优势,动态代理不仅不需要定义代理类,甚至可以在运行时指定代理类的执行逻辑,从而大大提升系统的灵活性。
目前动态代理类的生成方法有很多,有JDK自带的动态代理、CGLIB、Javassist和ASM库等。
- JDK动态代理:内置在JDK中,不需要引入第三方jar,使用简单,但功能比较弱。
- CGLIB/Javassist:这两个都是高级的字节码生成库,总体性能比JDK动态代理好,且功能强大。
- ASM:低级字节码生成工具,近乎使用bytecode编码,对开发人员要求最高。当然性能也是最好(相比前几种也不是很大的提升,这里不做具体介绍)。
以下实例依然以SmsSender
和ISender
作为被代理对象和接口进行试验。
1) JDK动态代理
JDK的动态代理需要实现一个处理方法调用的Handler,用于实现代理方法的内部逻辑,实现InvocationHandler
接口。
public class JdkProxyHandler implements InvocationHandler { private Object target; public JdkProxyHandler(Object target){ this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("处理前"); Object result = method.invoke(target,args); System.out.println("处理后"); return result; } }
客户端调用:
@Test public void testJdkProxy(){ ISender sender = (ISender) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{ISender.class}, new JdkProxyHandler(new SmsSender())); boolean result = sender.send(); System.out.println("代理对象:" + sender.getClass().getName()); System.out.println("输出结果:" + result); }
输出结果:
处理前
sending msg
处理后
代理对象:com.sun.proxy.$Proxy4
输出结果:true
这样实现一个简单的AOP就完成了,我们看到代理类的类型是com.sun.proxy.$Proxy4
。那JDK是如何创建代理类?
首先从Proxy.newProxyInstance入手,来研究JDK是如何生成代理类:
public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h)
该方法有3个参数:
- loader:用哪个类加载器去加载代理对象,生成目标对象的代理需要确保其类加载器相同,所以需要将目标对象的类加载器作为参数传递。
- interfaces:代理类需实现的接口列表,JDK动态代理技术需要代理类和目标对象都继承自同一接口,所以需要将目标对象的接口作为参数传递。
- h:调用处理器,调用实现了InvocationHandler类的一个回调方法,对目标对象的增强逻辑在这个实现类中。
具体代码如下:
public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h) throws IllegalArgumentException { //1.检查 Objects.requireNonNull(h); final Class>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * Look up or generate the designated proxy class. */ //获取代理类类型 Class> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } //通过反射创建代理对象 final Constructor> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h;以上就是Java动态代理四种实现方式详解的详细内容,更多请关注0133技术站其它相关文章!