CGlib动态代理
一、CGlib介绍
参考链接:设计模式之代理模式https://www.cnblogs.com/lixiuyu/p/5919643.html
CGlib(Code Generation Library)是一个强大的,高性能的代码生成库。
它被广泛使用在基于代理的AOP框架(例如Spring AOP和dynaop)提供方法拦截(interception)。
它可以在运行期扩展Java类与实现Java接口。(jdk中的proxy必须基于接口,cglib却没有这个限制。)
CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。
二、CGlib动态代理实例
1、定义一个服务类,不实现任何接口,有两个方法,其中一个方法用final修饰。
(如果委托类被最终修饰,那么它不可被继承,即不可被代理;
同样,如果委托类中存在final修饰的方法,那么该方法也不可被代理;)
public class HelloService {
public HelloService() {
System.out.println("HelloService构造");
}
/**
* 该方法不能被子类覆盖,Cglib是无法代理final修饰的方法的
*/
final public String sayOthers(String name) {
System.out.println("HelloService:sayOthers>>"+name);
return null;
}
public void sayHello() {
System.out.println("HelloService:sayHello");
}
}
2、定义一个自定义MethodInterceptor (可以在intercept方法中实现权限拦截等)
public class MyMethodInterceptor implements MethodInterceptor{
/**
* sub:cglib生成的代理对象
* method:被代理对象方法
* objects:方法入参
* methodProxy: 代理方法
*/
@Override
public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("======插入前置通知======");
Object object = methodProxy.invokeSuper(sub, objects);
System.out.println("======插入后者通知======");
return object;
}
}
3、生成CGLIB代理对象调用目标方法
public class Client {
public static void main(String[] args) {
// 代理类class文件存入本地磁盘方便我们反编译查看源码
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
// 通过CGLIB动态代理获取代理对象的过程
Enhancer enhancer = new Enhancer();
// 设置enhancer对象的父类
enhancer.setSuperclass(HelloService.class);
// 设置enhancer的回调对象
enhancer.setCallback(new MyMethodInterceptor());
// 创建代理对象
HelloService proxy= (HelloService)enhancer.create();
// 通过代理对象调用目标方法
proxy.sayHello();
}
}
4、运行结果
HelloService构造
======插入前置通知======
HelloService:sayHello
======插入后者通知======
三、分析
调用过程:
代理对象调用this.sayHello方法->调用拦截器->methodProxy.invokeSuper->CGLIB$sayHello$0->被代理对象的sayHello方法。
拦截器MethodInterceptor中就是由MethodProxy的invokeSuper方法调用代理方法的。
Cglib动态代理执行代理方法效率之所以比JDK的高是因为Cglib采用了FastClass机制,它的原理简单来说就是:为代理类和被代理类各生成一个Class,这个Class会为代理类或被代理类的方法分配一个index(int类型)。 这个index当做一个入参,FastClass就可以直接定位要调用的方法直接进行调用,这样省去了反射调用。
参考链接:
(源码分析)
https://blog.****.net/yhl_jxy/article/details/80633194
https://www.cnblogs.com/monkey0307/p/8328821.html
(实例,CallbackFilter)