【动态代理】CGLIB 动态代理的使用及原理
1. CGLIB 动态代理介绍
什么是 CGLIB?
CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。
通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB 是一个好的选择。
CGLIB 的原理
CGLIB 原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是 final 的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。
CGLIB 底层:采用ASM字节码生成框架,使用字节码技术生成代理类,比使用 Java 反射效率要高。
2. CGLIB 动态代理使用
CGLIB 动态代理步骤:
- 引入 CGLIB 依赖
- 定义一个被代理类
- 定义一个拦截器并实现接口
MethodInterceptor
- 代理工厂类
- 通过代理对象调用方法
引入依赖: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();}
}
说明:
- 生成的动态代理类继承了父类
Student
,并且实现了接口Factory
- 动态代理类持有
MethodInterceptor
- 动态代理类会重写父类
Student
的非 final、private 方法;也会构建自己的方法(cglib 方法),构建方式:CGLIB”+“$父类方法名$
- cglib 方法的方法体:
super.方法名
,直接调用父类;重写方法:它会调用拦截器中的intercept()
方法 methodProxy.invokeSuper()
方法会调用动态代理类中的 cglib 方法;methodProxy.invoke()
方法会调用动态代理类中的重写方法
CGLIB 动态代理原理:外界调用了方法后(studentProxy.handOut();
),由于父类 Student
被子类(动态代理类)给继承了(已经重写了 handOut()
),所以,会调用动态代理类中的 handOut()
方法。而在这个重写的方法中,又会去调用 MethodInterceptor#intercept()
方法。在这个方法中,功能增强后,再去调用动态代理中的 cglib 方法,而此方法又会去调用父类中的方法。
4. JDK 动态代理和 CGLIB 动态代理比较
4.1 区别
总结一下两者的区别吧:
- JDK 动态代理基于接口,CGLIB 动态代理基于类。因为 JDK 动态代理生成的代理类需要继承
java.lang.reflect.Proxy
,所以,只能基于接口;CGLIB 动态代理是根据类创建此类的子类,所以,此类不能被final
修饰 - JDK 和 CGLIB 动态代理都是在运行期生成字节码。而 JDK 是直接写 Class 字节码;而 CGLIB 使用
ASM
框架写 Class 字节码(不鼓励直接使用ASM,因为它要求你必须对 JVM 内部结构包括 class 文件的格式和指令集都很熟悉) - JDK 通过反射调用方法,CGLIB 通过 FastClass 机制(下一篇再将)直接调用方法。所以,CGLIB 执行的效率较高
- JDK 动态代理是利用反射机制生成一个实现代理接口的类(这个类看不见摸不着,在 jvm 内存中有这个类),在调用具体方法前调用
InvokeHandler
来处理。核心是实现InvocationHandler
接口,使用invoke()
方法进行面向切面的处理,调用相应的通知;CGLIB 动态代理是利用 asm 开源包,对代理对象类的 class 文件加载进来,通过修改其字节码生成子类来处理。核心是实现MethodInterceptor
接口,使用intercept()
方法进行面向切面的处理,调用相应的通知。
4.2 优缺点
劣势:
- JDK:JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理
- CGLIB:CGLIB 的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对 final 修饰的类进行代理
优势:
- JDK:最小化依赖关系,减少依赖意味着简化开发和维护,JDK本身的支持,可能比 cglib 更加可靠
- JDK:平滑进行JDK版本升级,而字节码类库通常需要进行更新以保证在新版Java 上能够使用。代码实现简单
- CGLIB:从某种角度看,限定调用者实现接口是有些侵入性的实践,类似cglib动态代理就没有这种限制。只操作我们关心的类,而不必为其他相关类增加工作量。另外高性能。
5. 动态代理在 Spring 中的应用
Spring 应用:
- 如果目标对象实现了接口,默认情况下 Spring 会采用 JDK 的动态代理实现 AOP
- 如果目标对象实现了接口,Spring 也可以强制使用 CGLIB 实现 AOP
- 如果目标对象没有实现接口,必须采用 CGLIB 实现动态代理,当然 Spring 可以在 JDK 动态代理和 CGLIB 动态代理之间转换
【动态代理】CGLIB 动态代理的使用及原理相关推荐
- 静态代理,cglib动态代理,jdk动态代理区别以及流程详解
1.静态代理 静态代理使用的是代理设计模式,不讲高大上的思想,我们直接实战 这是动物接口,其中有一个吃饭方法 这是其中的一只动物,实现了动物接口,覆盖了吃饭方法 现在我们思考,我想要给猫找一个代理,希 ...
- java jdk动态代理 cglib动态代理demo
最近在研究java动态代理这块,以前也看了很多次java动态代理,感觉一直不是怎么明白,这两天看了看又明白了些,现给出我参考网上写的一个demo jdk动态代理实现: View Code import ...
- Java设计模式(五)代理设计模式—静态代理—JDK动态代理—Cglib动态代理
文章目录 什么是代理模式 代理模式应用场景 代理的分类 静态代理 什么是静态代理 深入解析静态代理 小结 动态代理 什么是动态代理 JDK动态代理 原理和实现方式 代码实现 优缺点 Cglib动态代理 ...
- 动态代理——CGLIB动态代理原理示例解析
觉得可以的话点个关注,转个发呗,陆续奉上干货~~~~ 前文我们讲解了JDK动态代理的原理(动态代理--JDK动态代理原理),今天我们来看看CGLIB动态代理是如何实现,最后我们总结下JDK动态代理和C ...
- 你必须会的 JDK 动态代理和 CGLIB 动态代理
来自:ytao 我们在阅读一些 Java 框架的源码时,基本上常会看到使用动态代理机制,它可以无感的对既有代码进行方法的增强,使得代码拥有更好的拓展性.通过从静态代理.JDK 动态代理.CGLIB 动 ...
- Java动态代理的两种实现方法:JDK动态代理和CGLIB动态代理
Java动态代理的两种实现方法:JDK动态代理和CGLIB动态代理 代理模式 JDK动态代理 CGLIB动态代理 代理模式 代理模式是23种设计模式的一种,指一个对象A通过持有另一个对象B,可以具有B ...
- JDK动态代理与CGLIB动态代理区别
JDK动态代理 只能代理实现了接口的类 没有实现接口的类不能实现JDK的动态代理 CGLIB动态代理 针对类来实现代理的 对指定目标类产生一个子类,通过方法拦击技术拦截所有父类方法的调用 使用Cgli ...
- 利用代码分别实现jdk动态代理和cglib动态代理_面试之动态代理
大家好!我是CSRobot,从今天开始,我将会发布一些技术文章,内容就是结合春招以来的面试所遇到的问题进行分享,首先会对知识点进行一个探讨和整理,在最后会给出一些面试题并作出解答,希望可以帮助到大家! ...
- 一文理解JDK静态代理、JDK动态代理、Cglib动态代理
代理模式 通过代理来访问真实的对象,而不是直接去访问真正干活的对象,比如二房东租房,二房是代理者,而一房东才是真正的房东:或者说生活中的中介.Spring中的AOP就是动态代理 适用场景 需要动态修改 ...
- java中的静态、动态代理模式以及Spring中的CgLib动态代理解读(面试必问)
java中的静态.动态代理模式以及Spring中的CgLib动态代理解读(面试必问) 静态代理 动态代理 CgLib动态代理 基础知: 反射知识 代理(Proxy)是一种设计模式,提供了对目标 ...
最新文章
- [渝粤教育] 西南科技大学 网络程序设计 在线考试复习资料
- VC6.0背景颜色更改
- 网络基础一(协议的概念,网络应用程序设计模式)
- Chrome Workspace开发者调试工具
- oracle sql优化
- python接口自动化(二十一)--unittest简介(详解)
- Linux根据启动程序文件名称进行批量结束任务
- 苹果Mac Finder 替代工具:Path Finder
- vue跨域/webpack跨域
- 2010考研数学二第(20)题——多元积分学:二重积分计算
- centos安装aria2c_CentOS安装aria2 + yaaw实现离线下载
- ios 根据日期知道周几_iOS-通过日期计算是周几
- FORM 6i 安装
- Mysql CASE方法条件怎么加and或or
- 【性能测试】获取性能系统指标之示例Python代码
- 2.电调(ESC)-XP7A刷BLHeli固件(四轴专用,更快响应)
- 微软校招面试题3-15
- AOP中的概念通知、切点、切面、
- 嵌入式系统编程实现485串口收发数据
- 单行文本溢出省略号,多行文本溢出省略号,多行文本折行(可用)
热门文章
- 【原创开源应用第1期】花式玩转网络摄像头之TCP上位机软件实现,高端大气上档次,速度2MB/S,华丽丽的界面效果
- 彻底关闭电脑系统更新提示!
- 前端发送请求到后端报400错误问题
- 社区发现FN算法Python实现
- 关于计算机的英语手抄报简单,英语交通工具手抄报,十分简单的英语手抄报?...
- 传送门骑士无限物品的服务器,传送门骑士怎么无限刷资源 无限资源获得方法...
- web前端导航条制作
- 原来谷歌有个“谷歌编程学院”-google code university
- 8数码无解,拼图问题
- Java中entryset用法,keySet()、entrySet()和Map.Entry用法