神通广大的cglib和基于它实现的动态代理

标签: Java, 设计模式

保留所有版权,请引用而不是转载本文(原文地址 https://yeecode.top/blog/62/ )。

前面的文章中我们已经介绍了基于反射的动态代理。但是它有一个制约条件,即被代理的类必须要有一个父接口。但是有些类确实没有父接口,对于这些类而言,基于反射的动态代理是不适用的。

本文我们介绍另一种实现动态代理的方式:基于cglib(Code Generation Library,即代码生成库) 的动态代理。

一个类必须要通过类加载过程将类文件加载到JVM后才能使用。那是否能够直接修改JVM中的字节码信息来修改和创建类呢?

答案是可以的,cglib就是基于这个原理工作的。cglib使用字节码处理框架ASM来转换字节码并生成被代理类的子类。然后这个子类就可以作为代理类展开工作。ASM是一个非常底层的框架,除非你对JVM内部结构包括class文件的格式和指令集都很熟悉,否则不要直接使用ASM。

接下来我们通过示例介绍一下如何用cglib实现动态代理。

首先要在项目中引入cglib工具包,以使用Maven为例,在pom文件中增加下面所示的依赖。

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.9</version>
</dependency>

被代理类不需要实现任何接口,下面代码给出了一个简单的被代理类。

public class User{
    public String sayHello(String name) {
        System.out.println("hello " + name);
        return "OK";
    }
}

接下来我们编写一个实现了org.springframework.cglib.proxy.MethodInterceptor接口的类,如下所示。

public class ProxyHandler<T> implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before speak");
        Object ans = methodProxy.invokeSuper(o, objects);
        System.out.println("after speak");
        return ans;
    }
}

被代理类中的方法被拦截后,会进入该类中intercept方法。在该类的intercept方法中,我们在被代理对象的方法执行前后各增加了一句输出语句。

之后我们就可以建立代理对象,实现动态代理操作,该过程如下所示。

public static void main(String[] args) throws Exception {
    Enhancer enhancer = new Enhancer();
    // 设置enhancer的回调对象
    enhancer.setCallback(new ProxyHandler<>());
    // 设置enhancer对象的父类
    enhancer.setSuperclass(User.class);
    // 创建代理对象,实际为User的子类
    User user = (User) enhancer.create();

    // 通过代理对象调用目标方法
    String ans = user.sayHello("易哥");
    System.out.println(ans);
}

我们创建了一个代理对象(即变量user对应的对象),然后调用了其中的方法。最终得到了下图所示的运行结果。

图 22-1 程序运行结果

可见,在被代理对象的方法执行前后,均输出了代理对象中增加的语句。

cglib是通过给被代理类创建一个子类,从而实现在不改变被代理类的情况下创建代理类的。因此它也有一定的局限性:也就无法为final类创建代理,因为final类没有子类。

MyBatis在进行对象的懒加载时就有使用cglib创建目标对象的代理对象的过程。其具体过程本文不再展开,大家可以参考《通用源码阅读指导书——MyBatis源码详解》一书的“22.3 懒加载功能”章节,该章节对MyBatis的懒加载相关源码进行了详细的介绍,读完会很有收获。

通用源码阅读指导书-京东自营

《通用源码阅读指导书》

可以访问个人知乎阅读更多文章:易哥(https://www.zhihu.com/people/yeecode),欢迎关注。

作者书籍推荐