synology建设网站,wordpress设置多域名多站点,微信推广引流加精准客户,摄影网站模板文章目录 1. 简介2. 示例3. 原理4. JDK动态代理与CGLIB动态代理区别#xff08;面试常问#xff09; 1. 简介
CGLIB的全称是#xff1a;Code Generation Library。
CGLIB是一个强大的、高性能、高质量的代码生成类库#xff0c;它可以在运行期扩展Java类与实现Java接口面试常问 1. 简介
CGLIB的全称是Code Generation Library。
CGLIB是一个强大的、高性能、高质量的代码生成类库它可以在运行期扩展Java类与实现Java接口 底层使用的是字节码处理框架ASM。
Github地址github.com/cglib/cglib。
CGLIB的Maven坐标如下所示
dependencygroupIdcglib/groupIdartifactIdcglib/artifactIdversion3.3.0/version
/dependency2. 示例
首先新增一个类
public class Coder {public void work() {System.out.println(认真写bug……);}
}然后自定义一个方法拦截器实现net.sf.cglib.proxy.MethodInterceptor接口并重写intercept方法
public class AttendanceMethodInterceptor implements MethodInterceptor {Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println(上班打卡……);Object result proxy.invokeSuper(obj, args);System.out.println(下班打卡……);return result;}
}重点看下Object result proxy.invokeSuper(obj, args);该行代码最终会执行真正的目标方法在这前后我们可以添加一些增强逻辑。
然后新建个测试类看下CGLIB动态代理如何使用
public class CglibProxyTest {public static void main(String[] args) {Enhancer enhancer new Enhancer();enhancer.setSuperclass(Coder.class);enhancer.setCallback(new AttendanceMethodInterceptor());// 创建代理对象Object object enhancer.create();Coder coder (Coder) object;coder.work();}
}运行以上代码效果如下图所示
从运行结果可以看出在目标方法的前后执行了自定义的操作。
3. 原理
看下上面的测试类代码首先是创建了一个net.sf.cglib.proxy.Enhancer对象然后调用了setSuperclass()方法将enhancer对象的父类设置为Coder类
紧接着调用了setCallback()方法将enhancer对象的方法拦截器设置为自定义的AttendanceMethodInterceptor
然后是调用enhancer对象的create()方法来生成一个代理对象。
先打印下简单看下这个代理类的信息
图中的com.zwwhnly.mybatisplusdemo.cglibproxy.Coder$$EnhancerByCGLIB$$8e91f654就是CGLIB生成的代理类的名称。
那么这个代理类具体是什么样子呢
在上面的测试类代码中Object object enhancer.create();代码之前添加以下一行代码
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, ./cglib);然后再次运行会看到项目根目录下生成了一个cglib文件夹自动生成的代理类就包含在其中
可以看到一共生成了5个类这里重点关注下红色标记的3个类。
先看下Coder$$EnhancerByCGLIB$$8e91f654.class这个类就是自动生成的代理类
可以看出Coder$$EnhancerByCGLIB$$8e91f654.class继承了Coder类也就是说自动生成的代理类其实是被代理类的一个子类并且重写了Coder类的work()方法重写后的work()方法会调用自定义的方法拦截器AttendanceMethodInterceptor里的intercept()方法。
然后看下Coder$$EnhancerByCGLIB$$8e91f654$$FastClassByCGLIB$$4e5eb5aa从名称上可以看出这个类的前半段和上面的类的名称是一样的后半段拼接上了$$FastClassByCGLIB$$4e5eb5aa从功能上说这个类是上面的代理类的索引类重点关注下里面的getIndex()方法和invoke()方法
最后看下Coder$$FastClassByCGLIB$$398819d0这个类是被代理类Coder的索引类重点也是关注下里面的getIndex()方法和invoke()方法
知道了这3个类的作用后再一步一步看下示例代码中coder.work();的调用过程因为coder是生成的代理类的实例所以coder.work();首先调用的是Coder$$EnhancerByCGLIB$$8e91f654的work()方法
这里的var10000是自定义的方法拦截器AttendanceMethodInterceptor所以执行的是红色截图里的intercept()方法也就是
然后看下invokeSuper()方法
首先执行的是init()方法在该方法内部对fastClassInfo字段进行了赋值
从上图可以看出fci.f1是自动生成的Coder类的索引类Coder$$FastClassByCGLIB$$398819d0所以fci.i1 fci.f1.getIndex(sig1);其实执行的是的Coder$$FastClassByCGLIB$$398819d0的getIndex()方法
fci.f2是自动生成的代理类的索引Coder$$EnhancerByCGLIB$$8e91f654$$FastClassByCGLIB$$4e5eb5aa 所以fci.i2 fci.f2.getIndex(sig2);其实执行的是的Coder$$EnhancerByCGLIB$$8e91f654$$FastClassByCGLIB$$4e5eb5aa的getIndex()方法
看完init()方法后再回到invokeSuper()方法
上图中的FastClassInfo fci fastClassInfo;使用到的字段fastClassInfo在init()方法内部已经赋过值 fci.f2其实是自动生成的代理类的索引类Coder$$EnhancerByCGLIB$$8e91f654$$FastClassByCGLIB$$4e5eb5aafci.i2值是1
所以fci.f2.invoke(fci.i2, obj, args);实际执行的是
这里的var10000其实是自动生成的代理类Coder$$EnhancerByCGLIB$$8e91f654的实例所以接着调用的是 代理类Coder$$EnhancerByCGLIB$$8e91f654的CGLIB$work$0()方法
这里的super指的是Coder类所以super.work();实际执行的是Coder类的work()方法
综上所述coder.work();的调用顺序依次是 代理类—自定义方法拦截器—代理类索引类getIndex()方法–代理类索引类invoke()方法—代理类—被代理类。 4. JDK动态代理与CGLIB动态代理区别面试常问 关于JDK动态代理可以查看上一篇博客JDK动态代理原理。 了解了JDK动态代理和CGLIB动态代理的原理后现在来比较下两者的区别这也是面试时几乎必问的一道面试题。 使用JDK动态代理被代理类必须要实现接口使用CGLIB动态代理被代理类可以不实现接口 原因分析 JDK动态代理生成的代理类继承了java.lang.reflect.Proxy因为Java是单继承的如果不通过实现接口的形式 无法对类进行扩展。 CGLIB动态代理生成的代理类实际上是被代理类的子类所以被代理类可以不实现接口。 自动生成类的数量不同 JDK动态代理只会生成1个代理类一般情况下名称为com.sun.proxy.$Proxy0。 CGLIB动态代理会生成好几个类核心的3个分别是 代理类被代理类的子类名称格式为Coder$$EnhancerByCGLIB$$8e91f654包名和被代理类包名一致。代理类的索引类名称格式为Coder$$EnhancerByCGLIB$$8e91f654$$FastClassByCGLIB$$4e5eb5aa 包名和被代理类包名一致。被代理类的索引类名称格式为Coder$$FastClassByCGLIB$$398819d0包名和被代理类包名一致。 生成代理类技术不同 JDK动态代理使用JDK自带的ProxyGenerator类生成字节码文件。 CGLIB动态代理使用ASM框架生成字节码文件。 调用方式不同 JDK动态代理代理类—InvocationHandler.invoke()—被代理类方法用到了反射。 CGLIB动态代理代理类—MethodInterceptor.intercept()—代理类索引类getIndex()— 代理类索引类invoke()—代理类—被代理类。直接调用