1. CGLIB 动态代理介绍

什么是 CGLIB?

CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。

通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB 是一个好的选择。

CGLIB 的原理

CGLIB 原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是 final 的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。

CGLIB 底层:采用ASM字节码生成框架,使用字节码技术生成代理类,比使用 Java 反射效率要高。

2. CGLIB 动态代理使用

CGLIB 动态代理步骤:

  1. 引入 CGLIB 依赖
  2. 定义一个被代理类
  3. 定义一个拦截器并实现接口 MethodInterceptor
  4. 代理工厂类
  5. 通过代理对象调用方法

引入依赖:cglib-nodep-2.2.jar

Student:被代理类

public class Student {public void handOut() {System.out.println("学生交作业。");}
}

CglibProxy:拦截器

public class CglibProxy implements MethodInterceptor {/*** @param o: 代理对象* @param method: 被代理方法* @param params: 方法入参* @param methodProxy: CGLIB方法**/@Overridepublic Object intercept(Object o, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {System.out.println("【增强方法】代理对象正在执行的方法:" + method.getName());Object result = methodProxy.invokeSuper(o, params);return result;}
}

CglibProxyFactory:代理工厂类

public class CglibProxyFactory {public static Object creatCglibProxyObj(Class<?> clazz) {Enhancer enhancer = new Enhancer();// 为加强器指定要代理的业务类(即为下面生成的代理类指定父类)enhancer.setSuperclass(clazz);// 设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法enhancer.setCallback(new CglibProxy());return enhancer.create();}}

测试:

public class Test {public static void main(String[] args) {Student studentProxy = (Student)CglibProxyFactory.creatCglibProxyObj(Student.class);studentProxy.handOut();}}

运行后,依旧可以增强原功能。

3. CGLIB 动态代理原理

上文中的是通过 enhancer.create 方法调用获取的代理对象,以此为入口深入探究一下 CGLIB 动态代理的实现原理。

Enhancer#create()

public Object create() {this.classOnly = false;this.argumentTypes = null;return this.createHelper();
}

Enhancer#createHelper():调用父类的 create() 方法

private Object createHelper() {//...return super.create(KEY_FACTORY.newInstance(this.superclass != null ? this.superclass.getName() : null, ReflectUtils.getNames(this.interfaces), this.filter, this.callbackTypes, this.useFactory, this.interceptDuringConstruction, this.serialVersionUID));
}

AbstractClassGenerator#create()

protected Object create(Object key) {try {//...if (gen == null) {// 1.生成代理类 byte[] b = this.strategy.generate(this);// 2.获取代理类名称String className = ClassNameReader.getClassName(new ClassReader(b));this.getClassNameCache(loader).add(className);gen = ReflectUtils.defineClass(className, b, loader);}if (this.useCache) {((Map)cache2).put(key, new WeakReference(gen));}var24 = this.firstInstance(gen);} finally {CURRENT.set(save);}return var24;}//...
}

DefaultGeneratorStrategy#generate():生成代理类

public byte[] generate(ClassGenerator cg) throws Exception {ClassWriter cw = this.getClassWriter();this.transform(cg).generateClass(cw);return this.transform(cw.toByteArray());
}

DebuggingClassWriter#toByteArray()

public byte[] toByteArray() {return (byte[])((byte[])AccessController.doPrivileged(new PrivilegedAction() {public Object run() {byte[] b = DebuggingClassWriter.super.toByteArray();if (DebuggingClassWriter.debugLocation != null) {String dirs = DebuggingClassWriter.this.className.replace('.', File.separatorChar);try {// 如果 DebuggingClassWriter.DEBUG_LOCATION_PROPERTY 系统属性被设置,则输出代理类到指定目录(new File(DebuggingClassWriter.debugLocation + File.separatorChar + dirs)).getParentFile().mkdirs();File file = new File(new File(DebuggingClassWriter.debugLocation), dirs + ".class");BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));try {out.write(b);} finally {out.close();}if (DebuggingClassWriter.traceEnabled) {file = new File(new File(DebuggingClassWriter.debugLocation), dirs + ".asm");out = new BufferedOutputStream(new FileOutputStream(file));try {ClassReader cr = new ClassReader(b);PrintWriter pw = new PrintWriter(new OutputStreamWriter(out));TraceClassVisitor tcv = new TraceClassVisitor((ClassVisitor)null, pw);cr.accept(tcv, 0);pw.flush();} finally {out.close();}}} catch (IOException var17) {throw new CodeGenerationException(var17);}}return b;}}));
}

生成 CGLIB 字节码文件

由上文可知,把 DebuggingClassWriter.DEBUG_LOCATION_PROPERTY(也就是 cglib.debugLocation)系统属性设置为当前项目的根目录,即可保存 CGLIB 生成的代理类到当前项目根目录下。

设置系统属性配置:

public static void main(String[] args) {System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, System.getProperty("user.dir"));Student studentProxy = (Student)CglibProxyFactory.creatCglibProxyObj(Student.class);studentProxy.handOut();
}

运行代码:

生成的动态代理类为:

public class Student$$EnhancerByCGLIB$$723acbd8 extends Student implements Factory {private boolean CGLIB$BOUND;private static final ThreadLocal CGLIB$THREAD_CALLBACKS;private static final Callback[] CGLIB$STATIC_CALLBACKS;private MethodInterceptor CGLIB$CALLBACK_0;private static final Method CGLIB$handOut$0$Method;private static final MethodProxy CGLIB$handOut$0$Proxy;//...static void CGLIB$STATICHOOK1() {CGLIB$THREAD_CALLBACKS = new ThreadLocal();CGLIB$emptyArgs = new Object[0];Class var0 = Class.forName("com.zzc.proxy.cglib.Student$$EnhancerByCGLIB$$723acbd8");Class var1;Method[] var10000 = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());CGLIB$handOut$0$Method = ReflectUtils.findMethods(new String[]{"handOut", "()V"}, (var1 = Class.forName("com.zzc.proxy.cglib.Student")).getDeclaredMethods())[0];CGLIB$handOut$0$Proxy = MethodProxy.create(var1, var0, "()V", "handOut", "CGLIB$handOut$0");//...}final void CGLIB$handOut$0() {super.handOut();}public final void handOut() {MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_0;}if (var10000 != null) {var10000.intercept(this, CGLIB$handOut$0$Method, CGLIB$emptyArgs, CGLIB$handOut$0$Proxy);} else {super.handOut();}}//...static {CGLIB$STATICHOOK1();}
}

说明:

  1. 生成的动态代理类继承了父类 Student,并且实现了接口 Factory
  2. 动态代理类持有 MethodInterceptor
  3. 动态代理类会重写父类 Student 的非 final、private 方法;也会构建自己的方法(cglib 方法),构建方式:CGLIB”+“$父类方法名$
  4. cglib 方法的方法体:super.方法名,直接调用父类;重写方法:它会调用拦截器中的 intercept() 方法
  5. methodProxy.invokeSuper() 方法会调用动态代理类中的 cglib 方法;methodProxy.invoke() 方法会调用动态代理类中的重写方法

CGLIB 动态代理原理:外界调用了方法后(studentProxy.handOut();),由于父类 Student 被子类(动态代理类)给继承了(已经重写了 handOut()),所以,会调用动态代理类中的 handOut() 方法。而在这个重写的方法中,又会去调用 MethodInterceptor#intercept() 方法。在这个方法中,功能增强后,再去调用动态代理中的 cglib 方法,而此方法又会去调用父类中的方法。

4. JDK 动态代理和 CGLIB 动态代理比较

4.1 区别

总结一下两者的区别吧:

  1. JDK 动态代理基于接口,CGLIB 动态代理基于类。因为 JDK 动态代理生成的代理类需要继承 java.lang.reflect.Proxy,所以,只能基于接口;CGLIB 动态代理是根据类创建此类的子类,所以,此类不能被 final 修饰
  2. JDK 和 CGLIB 动态代理都是在运行期生成字节码。而 JDK 是直接写 Class 字节码;而 CGLIB 使用 ASM 框架写 Class 字节码(不鼓励直接使用ASM,因为它要求你必须对 JVM 内部结构包括 class 文件的格式和指令集都很熟悉)
  3. JDK 通过反射调用方法,CGLIB 通过 FastClass 机制(下一篇再将)直接调用方法。所以,CGLIB 执行的效率较高
  4. JDK 动态代理是利用反射机制生成一个实现代理接口的类(这个类看不见摸不着,在 jvm 内存中有这个类),在调用具体方法前调用 InvokeHandler来处理。核心是实现 InvocationHandler接口,使用 invoke()方法进行面向切面的处理,调用相应的通知;CGLIB 动态代理是利用 asm 开源包,对代理对象类的 class 文件加载进来,通过修改其字节码生成子类来处理。核心是实现 MethodInterceptor 接口,使用 intercept() 方法进行面向切面的处理,调用相应的通知。

4.2 优缺点

劣势:

  1. JDK:JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理
  2. CGLIB:CGLIB 的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对 final 修饰的类进行代理

优势:

  1. JDK:最小化依赖关系,减少依赖意味着简化开发和维护,JDK本身的支持,可能比 cglib 更加可靠
  2. JDK:平滑进行JDK版本升级,而字节码类库通常需要进行更新以保证在新版Java 上能够使用。代码实现简单
  3. CGLIB:从某种角度看,限定调用者实现接口是有些侵入性的实践,类似cglib动态代理就没有这种限制。只操作我们关心的类,而不必为其他相关类增加工作量。另外高性能。

5. 动态代理在 Spring 中的应用

Spring 应用:

  1. 如果目标对象实现了接口,默认情况下 Spring 会采用 JDK 的动态代理实现 AOP
  2. 如果目标对象实现了接口,Spring 也可以强制使用 CGLIB 实现 AOP
  3. 如果目标对象没有实现接口,必须采用 CGLIB 实现动态代理,当然 Spring 可以在 JDK 动态代理和 CGLIB 动态代理之间转换

【动态代理】CGLIB 动态代理的使用及原理相关推荐

  1. 静态代理,cglib动态代理,jdk动态代理区别以及流程详解

    1.静态代理 静态代理使用的是代理设计模式,不讲高大上的思想,我们直接实战 这是动物接口,其中有一个吃饭方法 这是其中的一只动物,实现了动物接口,覆盖了吃饭方法 现在我们思考,我想要给猫找一个代理,希 ...

  2. java jdk动态代理 cglib动态代理demo

    最近在研究java动态代理这块,以前也看了很多次java动态代理,感觉一直不是怎么明白,这两天看了看又明白了些,现给出我参考网上写的一个demo jdk动态代理实现: View Code import ...

  3. Java设计模式(五)代理设计模式—静态代理—JDK动态代理—Cglib动态代理

    文章目录 什么是代理模式 代理模式应用场景 代理的分类 静态代理 什么是静态代理 深入解析静态代理 小结 动态代理 什么是动态代理 JDK动态代理 原理和实现方式 代码实现 优缺点 Cglib动态代理 ...

  4. 动态代理——CGLIB动态代理原理示例解析

    觉得可以的话点个关注,转个发呗,陆续奉上干货~~~~ 前文我们讲解了JDK动态代理的原理(动态代理--JDK动态代理原理),今天我们来看看CGLIB动态代理是如何实现,最后我们总结下JDK动态代理和C ...

  5. 你必须会的 JDK 动态代理和 CGLIB 动态代理

    来自:ytao 我们在阅读一些 Java 框架的源码时,基本上常会看到使用动态代理机制,它可以无感的对既有代码进行方法的增强,使得代码拥有更好的拓展性.通过从静态代理.JDK 动态代理.CGLIB 动 ...

  6. Java动态代理的两种实现方法:JDK动态代理和CGLIB动态代理

    Java动态代理的两种实现方法:JDK动态代理和CGLIB动态代理 代理模式 JDK动态代理 CGLIB动态代理 代理模式 代理模式是23种设计模式的一种,指一个对象A通过持有另一个对象B,可以具有B ...

  7. JDK动态代理与CGLIB动态代理区别

    JDK动态代理 只能代理实现了接口的类 没有实现接口的类不能实现JDK的动态代理 CGLIB动态代理 针对类来实现代理的 对指定目标类产生一个子类,通过方法拦击技术拦截所有父类方法的调用 使用Cgli ...

  8. 利用代码分别实现jdk动态代理和cglib动态代理_面试之动态代理

    大家好!我是CSRobot,从今天开始,我将会发布一些技术文章,内容就是结合春招以来的面试所遇到的问题进行分享,首先会对知识点进行一个探讨和整理,在最后会给出一些面试题并作出解答,希望可以帮助到大家! ...

  9. 一文理解JDK静态代理、JDK动态代理、Cglib动态代理

    代理模式 通过代理来访问真实的对象,而不是直接去访问真正干活的对象,比如二房东租房,二房是代理者,而一房东才是真正的房东:或者说生活中的中介.Spring中的AOP就是动态代理 适用场景 需要动态修改 ...

  10. java中的静态、动态代理模式以及Spring中的CgLib动态代理解读(面试必问)

    java中的静态.动态代理模式以及Spring中的CgLib动态代理解读(面试必问) 静态代理 动态代理 CgLib动态代理     基础知: 反射知识 代理(Proxy)是一种设计模式,提供了对目标 ...

最新文章

  1. [渝粤教育] 西南科技大学 网络程序设计 在线考试复习资料
  2. VC6.0背景颜色更改
  3. 网络基础一(协议的概念,网络应用程序设计模式)
  4. Chrome Workspace开发者调试工具
  5. oracle sql优化
  6. python接口自动化(二十一)--unittest简介(详解)
  7. Linux根据启动程序文件名称进行批量结束任务
  8. 苹果Mac Finder 替代工具:Path Finder
  9. vue跨域/webpack跨域
  10. 2010考研数学二第(20)题——多元积分学:二重积分计算
  11. centos安装aria2c_CentOS安装aria2 + yaaw实现离线下载
  12. ios 根据日期知道周几_iOS-通过日期计算是周几
  13. FORM 6i 安装
  14. Mysql CASE方法条件怎么加and或or
  15. 【性能测试】获取性能系统指标之示例Python代码
  16. 2.电调(ESC)-XP7A刷BLHeli固件(四轴专用,更快响应)
  17. 微软校招面试题3-15
  18. AOP中的概念通知、切点、切面、
  19. 嵌入式系统编程实现485串口收发数据
  20. 单行文本溢出省略号,多行文本溢出省略号,多行文本折行(可用)

热门文章

  1. 【原创开源应用第1期】花式玩转网络摄像头之TCP上位机软件实现,高端大气上档次,速度2MB/S,华丽丽的界面效果
  2. 彻底关闭电脑系统更新提示!
  3. 前端发送请求到后端报400错误问题
  4. 社区发现FN算法Python实现
  5. 关于计算机的英语手抄报简单,英语交通工具手抄报,十分简单的英语手抄报?...
  6. 传送门骑士无限物品的服务器,传送门骑士怎么无限刷资源 无限资源获得方法...
  7. web前端导航条制作
  8. 原来谷歌有个“谷歌编程学院”-google code university
  9. 8数码无解,拼图问题
  10. Java中entryset用法,keySet()、entrySet()和Map.Entry用法