摘要:在之前的分享中,我们介绍了什么是基于接口的动态代理,基于接口的动态代理需要要求代理类实现某个接口,在某些情况下这种代理模式不够灵活。下面我们就来介绍一下基于类的动态代理,这种代理机制允许在运行时创建一个继承了目标类的子类来实现代理对象。因为在Java中本身支持
在之前的分享中,我们介绍了什么是基于接口的动态代理,基于接口的动态代理需要要求代理类实现某个接口,在某些情况下这种代理模式不够灵活。下面我们就来介绍一下基于类的动态代理,这种代理机制允许在运行时创建一个继承了目标类的子类来实现代理对象。因为在Java中本身支持的Proxy类值支持接口代理,所以基于类的动态代理一般需要第三方的库支持例如比较常用的CGLIB库。
首先我们需要明确,基于类的动态代理是通过继承目标类来创建了一个子类,然后这个代理的子类会覆盖目标类的所有方然,然后在方法的执行前后引入增强逻辑来进行处理。由于是子类,所以代理类也可以进行一些其他的处理。
在Java中我们可以通过CGLIB库来实现,CGLIB是一个功能强大的字节码生成库,而我们的Java类最终也会被转换为字节码文件,所以正好可以通过它来动态的生成目标类的子类,然后通过继承和覆盖的方式来实现基于类的动态代理。
下面我们就来详细介绍一下如何通过CGLIB来实现基于类的动态代理操作,如下所示,首先我们需要再代码中添加上CGLIB的依赖配置。
cglibcglib3.3.0接下来就来看看如何具体实现基于类的动态代理操作。
定义目标类
首先需要创建一个需要被代理目标类,如下所示。
public class RealSubject {public voidrequest {System.out.println("RealSubject: Handling request...");}}创建Methodinterceptor方法拦截器
接下来就是通过CGLIB的MethodInterceptor接口来创建方法拦截器用来拦截目标对象的方法调用,并且可以在intercept 方法中加入自定义的增强逻辑,如下所示。
import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;public class LoggingMethodInterceptor implements MethodInterceptor {// 目标对象private Object target;// 构造方法接收目标对象public LoggingMethodInterceptor(Object target) {this.target = target;}// 拦截方法调用@Overridepublic Object intercept(Object obj, java.lang.reflect.Method method, Object args, MethodProxy proxy) throws Throwable {// 在目标方法执行前记录日志System.out.println("Logging: Before method " + method.getName);// 调用目标对象的方法Object result = proxy.invokeSuper(obj, args);// 在目标方法执行后记录日志System.out.println("Logging: After method " + method.getName);return result;}}创建代理对象
在CGLIB中我们可以通过Enhancer 类来创建代理对象,通过Enhancer来创建一个目标子类,然后覆盖目标类的相关方法实现,如下所示。
import net.sf.cglib.proxy.Enhancer;public class ProxyExample {public static void main(String args) {// 创建目标对象RealSubject realSubject = new RealSubject;// 创建 MethodInterceptor,用于拦截方法LoggingMethodInterceptor interceptor = new LoggingMethodInterceptor(realSubject);// 创建代理对象Enhancer enhancer = new Enhancer;enhancer.setSuperclass(RealSubject.class); // 设置目标类enhancer.setCallback(interceptor); // 设置方法拦截器// 通过 CGLIB 创建代理对象RealSubject proxy = (RealSubject) enhancer.create;// 通过代理对象调用方法proxy.request;}}最终输出结果
最终的输出结果如下所示。
Logging: Before method requestRealSubject: Handling request...Logging: After method request到这里,我们就将一个普通的类RealSubject通过动态代理的方式进行了增强,并且在这个类中并没有继承什么其他的接口就直接通过CGLIB 的 MethodInterceptor 接口在 intercept 方法中加入了日志记录的逻辑。然后在后续调用代理对象的时候,就可以看到对代理类进行了增强。
与Java提供的基于接口的动态代理不同,CGLIB通过继承目标类的方式来实现代理,理论上来讲可以代理任何的对象,灵活性方面要比Java提供的基于接口的方式要更强一些,并且通过继承的实现子类的方式可以对目标类进行更多的复杂操作。
但是有优势就一定有劣势,由于是代理子类,所以对于final标记的类和方法就无法进行代理,其实这个也不是劣势,这是Java提供的一种保护机制。其次既然是代理子类,那么在生成类路径以及类加载器,类对象等方面就会出现一定的错误,所以在代理类的构造器上一般需要增加super的构造。另外就是生成字节码带来的系统开销。
基于类的动态代理在很多实际场景中都有用到,尤其是在需要代理那些没有实现接口的类时,通过CGLIB来实现基于类动态代理是一种非常有效的方案,例如在Spring框架中被大量的使用,有兴趣深入了解的读者可以阅读以下Spring AOP相关的源码实现。
来源:从程序员到架构师