Cglib动态代理

Cglib动态代理

使用Cglib提供的动态代理功能,我们需要在项目中引入Cglib依赖。

maven依赖配置文件中添加Cglib:

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

gradle依赖配置文件中添加Cglib:

compile group: 'cglib', name: cglib, version: '3.2.5'

Cglib是Code Generator Library的缩写,即代码生成库。Cglib通过使用ASM框架操作字节码控制对象的访问,提供方法拦截操作,它可以在运行期扩展Java类与实现Java接口,不仅可以实现基于接口的动态代理也可实现基于子类的动态代理。

我们还是以打印第三方API调用耗时为例,只是使用Cglib生成动态代理类我们不需要定义接口,可直接创建代理HttpRequestTemplateImpl类的动态代理类实例。如代码清单6-15所示。

代码清单6-15 使用cglib创建HttpRequestTemplateImpl代理对象

public class CglibAop {

    static {
        // 代理类class文件存入本地磁盘
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/tmp");
    }

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(HttpRequestTemplateImpl.class);
        enhancer.setCallback(new HttpRequestMethodInterceptor());
        HttpRequestTemplateImpl requestTemplate =
(HttpRequestTemplateImpl) enhancer.create();
}
}

代码清单6-15中,静态代码块的作用是为Cglib添加配置,让Cglib将动态生成的代理类的class字节码输出到文件,文件保存在tmp目录下。main方法中,首先创建一个增强器Enhancer,调用Enhancer实例的setSuperclass方法写入需要被代理的类,调用Enhancer实例的setCallback方法添加方法拦截器,最后调用Enhancer实例的create方法创建代理类实例。

HttpRequestMethodInterceptor方法拦截器的实现如代码清单6-16所示。

代码清单6-16 HttpRequestMethodInterceptor方法拦截器

public class HttpRequestMethodInterceptor implements MethodInterceptor {

    @Override
public Object intercept(Object obj, Method method, Object[] objects,
MethodProxy methodProxy) throws Throwable {
        long startMs = System.currentTimeMillis();
        try {
            return methodProxy.invokeSuper(obj,objects);
        } finally {
            long cntMs = System.currentTimeMillis() - startMs;
            System.out.println(method.getName() + "方法的执行耗时为" + cntMs + "毫秒");
        }
    }

}

Cglib为HttpRequestTemplateImpl类生成的动态代理类如代码清单6-17所示。

代码清单6-17 cglib生成的HttpRequestTemplateImpl代理类

public class HttpRequestTemplateImpl$$EnhancerByCGLIB$$303ac1f7
extends HttpRequestTemplateImpl implements Factory {
    ......
    private static final Method CGLIB$doGet$0$Method;
    private static final MethodProxy CGLIB$doGet$0$Proxy;
    ......

    final HttpResponse CGLIB$doGet$0(HttpRequest var1) {
        return super.doGet(var1);
    }

    public final HttpResponse doGet(HttpRequest var1) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        return var10000 != null ? (HttpResponse)var10000.intercept(this, CGLIB$doGet$0$Method, new Object[]{var1}, CGLIB$doGet$0$Proxy) : super.doGet(var1);
    }

    final HttpResponse CGLIB$doPost$1(HttpRequest var1) {
        return super.doPost(var1);
    }

    public final HttpResponse doPost(HttpRequest var1) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }
        return var10000 != null ? (HttpResponse)var10000.intercept(this,
CGLIB$doPost$1$Method, new Object[]{var1}, CGLIB$doPost$1$Proxy)
                          : super.doPost(var1);
    }
    ......
}

从Cglib生成的代理类可以看出,Cglib通过继承父类并重写父类的方法实现方法拦截。代理类重写父类的方法,先判断方法拦截器是否存在,存在则调用方法拦截器的invoker方法,否则直接调用父类的方法。

与基于接口的实现不同,因为接口本身没有实现方法,需要给方法拦截处理器传递一个接口的实现类对象,在方法拦截处理器中调用这个对象的方法。通过继承被代理类生成的动态代理类,代理类重写父类方法拦截父类方法的调用,我们不需要给方法拦截器传递一个父类实例的引用,只需要在方法拦截器中能调用父类的方法。

想要在方法拦截器中调用代理对象的父类方法,只能通过在代理类中使用super关键字调用父类的方法。因此,在为代理类重写父类方法时,也要为代理类添加一个调用父类方法的额外方法。从代码清单6-17中我们可以看出,Cglib为实现在方法拦截器中能够调用父类的方法,也为每一个重写的方法都生成了一个额外的方法,如为doGet方法生成一个CGLIB$doGet$0方法,为doPost方法生成一个CGLIB$doPost$1方法。