当前位置: 首页 > news >正文

JDK动态代理和CGLIB动态代理有什么区别?

JDK动态代理和CGLIB动态代理是Java中实现动态代理的两种常用机制,虽然它们都可以为目标对象创建代理对象并拦截方法调用,但它们的工作原理和使用场景有所不同。以下是它们的区别:

目录

一、基于接口 vs 基于类

二、实现机制

三、性能差异

四、使用场景

五、Spring AOP中的使用

六、优缺点对比

七、简单示例比较

JDK动态代理示例

CGLIB动态代理示例

比较 

八、总结


一、基于接口 vs 基于类

(1)JDK动态代理

基于接口:JDK动态代理要求被代理的类必须实现一个或多个接口。代理对象会实现这些接口,并将方法调用委托给目标对象。

如果类没有实现任何接口,JDK动态代理将无法工作。

(2)CGLIB动态代理

基于类:CGLIB动态代理通过生成目标类的子类来实现代理。它不要求目标类必须实现接口,因此它适用于没有实现接口的类。

CGLIB是通过继承方式创建代理类,因此不能代理`final`类或`final`方法,因为这些无法被继承和重写。

二、实现机制

(1)JDK动态代理

使用反射机制,通过 `java.lang.reflect.Proxy` 类和 `InvocationHandler` 接口来实现代理。代理对象仅代理接口中的方法。

当调用代理对象的方法时,代理类会拦截方法调用,并通过 `InvocationHandler.invoke()` 方法执行额外的逻辑。

因此,JDK动态代理实现原理:

  1. 首先通过实现InvocationHandler接口得到一个切面类。
  2. 然后利用Proxy根据目标类的类加载器、接口和切面类得到一个代理类。
  3. 代理类的逻辑就是把所有接口方法的调用转发到切面类的invoke0方法上,然后根据反射调用目标类的方法。

(2)CGLIB动态代理

基于字节码操作,它使用 CGLIB(Code Generation Library)生成目标类的子类并重写目标类的方法来实现代理。通过继承方式拦截所有非`final`方法的调用。

CGLIB 使用的是 ASM 字节码生成框架,生成的是字节码级别的代理类,因此性能相对较好,但生成代理类的开销比JDK动态代理略大。

三、性能差异

(1)JDK动态代理

对于实现了接口的类来说,JDK动态代理在创建代理对象时开销较小,因为它仅依赖反射机制来处理接口方法的调用。
对于频繁调用代理方法的场景,JDK动态代理可能比CGLIB略慢,因为每次调用都涉及反射。

(2)CGLIB动态代理

由于CGLIB是通过字节码生成来创建代理类,生成代理类的开销比JDK动态代理高一些,尤其是在代理类较多的情况下。

但CGLIB代理的实际方法调用性能更高,因为它通过字节码操作,减少了反射调用的开销。

四、使用场景

(1)JDK动态代理

适用于接口驱动的编程,如果目标类实现了接口,那么使用JDK动态代理是首选方式。

适合在不需要对类进行直接代理的场景,通常在应用中,业务逻辑往往是通过接口定义的,因此JDK代理在实际项目中更常用。

(2)CGLIB动态代理

适用于没有实现接口的类,例如一些现有类或者第三方库的类没有提供接口的情况下,可以使用CGLIB动态代理。

适用于对类进行代理时,但需要注意类不能是`final`,否则CGLIB无法生成代理子类。

五、Spring AOP中的使用

(1)JDK动态代理

在Spring AOP中,如果目标对象实现了接口,Spring默认使用JDK动态代理。这是因为Spring AOP的核心思想是基于接口的面向切面编程(Aspect-Oriented Programming)。

(2)CGLIB动态代理

如果目标对象没有实现任何接口,Spring AOP会自动使用CGLIB动态代理。在Spring配置中,你也可以强制使用CGLIB代理(通过设置`proxyTargetClass=true`)。

六、优缺点对比

七、简单示例比较

JDK动态代理示例

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;interface MyService {void serve();
}class MyServiceImpl implements MyService {public void serve() {System.out.println("Serving...");}
}class MyInvocationHandler implements InvocationHandler {private Object target;public MyInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before method: " + method.getName());Object result = method.invoke(target, args);System.out.println("After method: " + method.getName());return result;}
}public class JdkProxyDemo {public static void main(String[] args) {MyServiceImpl service = new MyServiceImpl();MyService proxy = (MyService) Proxy.newProxyInstance(service.getClass().getClassLoader(),service.getClass().getInterfaces(),new MyInvocationHandler(service));proxy.serve();}
}

在这个例子中,MyService是一个接口,MyServiceImpl实现了这个接口。JDK动态代理要求被代理的对象实现至少一个接口,这样代理类可以通过接口对外暴露代理后的行为。 

动态代理创建Proxy.newProxyInstance()方法用于创建代理对象,它需要三个参数:

  1. 类加载器service.getClass().getClassLoader(),用于加载代理类。
  2. 接口列表service.getClass().getInterfaces(),指定代理类应该实现的接口列表。
  3. InvocationHandler:代理对象的实际逻辑是通过实现InvocationHandler接口的类来实现的。在这里,MyInvocationHandler会拦截代理方法调用并添加额外的逻辑。

MyInvocationHandlerinvoke()方法中,方法调用被拦截,加入了"Before method"和"After method"的日志输出。在实际调用目标方法时,使用method.invoke(target, args)来执行目标对象的原始方法。

CGLIB动态代理示例

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;class MyService {public void serve() {System.out.println("Serving...");}
}class MyMethodInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("Before method: " + method.getName());Object result = proxy.invokeSuper(obj, args);System.out.println("After method: " + method.getName());return result;}
}public class CglibProxyDemo {public static void main(String[] args) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(MyService.class);enhancer.setCallback(new MyMethodInterceptor());MyService proxy = (MyService) enhancer.create();proxy.serve();}
}

MyService类并没有实现任何接口,这就是CGLIB动态代理的优势之一,它不要求目标类实现接口。CGLIB通过生成目标类的子类来实现代理。

CGLIB通过Enhancer类来创建代理对象,主要配置了两个部分:

  1. 目标类的超类:通过enhancer.setSuperclass(MyService.class)指定目标类(MyService)。
  2. 回调逻辑:通过enhancer.setCallback()设置方法拦截器MyMethodInterceptor,它会拦截所有方法调用,并可以插入额外的逻辑。

MyMethodInterceptor实现了MethodInterceptor接口,代理的核心逻辑在intercept()方法中。与JDK动态代理不同,CGLIB使用proxy.invokeSuper(obj, args)调用父类的原始方法,而不是通过反射调用。

比较 

八、总结

JDK动态代理更适合有接口的类,代理创建较快,但方法调用时性能略慢。CGLIB动态代理更适合没有接口的类,代理创建较慢,但方法调用时性能更高。

选择使用哪种代理方式,取决于你的对象是否实现了接口,以及性能和开发便利性的平衡。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 鸿蒙开发之ArkTS 基础三 数组
  • 国际商城系统怎么弄 跨境电商商城怎样上线
  • 网络安全产品认证证书大全(持续更新...)
  • YoloV10改进策略:BackBone改进|注意力改进|HCANet全局与局部的注意力模块CAFM|二次创新|即插即用
  • 代码随想录算法训练营day37
  • 7.Jmeter数据驱动(csv数据文件设置)+Jmeter数据库操作
  • Java:继承和多态(2)
  • 使用原生HTML的drag实现元素的拖拽
  • 【RAG】RAG再进化?基于长期记忆的检索增强生成新范式-MemoRAG
  • kitti数据深度图转点云坐标计算方法与教程(代码实现)
  • 关于Spring Cloud Gateway中 Filters的理解
  • 逻辑模型/物理模型
  • Flutter启动无法运行热重载
  • 部署opengauss5.0.3,细节满满
  • 力扣最热一百题——螺旋矩阵
  • 【JavaScript】通过闭包创建具有私有属性的实例对象
  • Android 控件背景颜色处理
  • JavaScript 奇技淫巧
  • java多线程
  • JS笔记四:作用域、变量(函数)提升
  • laravel5.5 视图共享数据
  • maya建模与骨骼动画快速实现人工鱼
  • Mithril.js 入门介绍
  • MyEclipse 8.0 GA 搭建 Struts2 + Spring2 + Hibernate3 (测试)
  • Python代码面试必读 - Data Structures and Algorithms in Python
  • scrapy学习之路4(itemloder的使用)
  • webpack入门学习手记(二)
  • 扫描识别控件Dynamic Web TWAIN v12.2发布,改进SSL证书
  • 使用Tinker来调试Laravel应用程序的数据以及使用Tinker一些总结
  • 数组的操作
  • 王永庆:技术创新改变教育未来
  • 学习ES6 变量的解构赋值
  • 远离DoS攻击 Windows Server 2016发布DNS政策
  • 自动记录MySQL慢查询快照脚本
  • FaaS 的简单实践
  • 分布式关系型数据库服务 DRDS 支持显示的 Prepare 及逻辑库锁功能等多项能力 ...
  • ​虚拟化系列介绍(十)
  • ​用户画像从0到100的构建思路
  • ###51单片机学习(2)-----如何通过C语言运用延时函数设计LED流水灯
  • #Js篇:单线程模式同步任务异步任务任务队列事件循环setTimeout() setInterval()
  • #我与Java虚拟机的故事#连载03:面试过的百度,滴滴,快手都问了这些问题
  • ${ }的特别功能
  • $HTTP_POST_VARS['']和$_POST['']的区别
  • (152)时序收敛--->(02)时序收敛二
  • (Bean工厂的后处理器入门)学习Spring的第七天
  • (k8s)Kubernetes本地存储接入
  • (不用互三)AI绘画:科技赋能艺术的崭新时代
  • (二)linux使用docker容器运行mysql
  • (附源码)springboot优课在线教学系统 毕业设计 081251
  • (附源码)计算机毕业设计SSM疫情社区管理系统
  • (十二)python网络爬虫(理论+实战)——实战:使用BeautfulSoup解析baidu热搜新闻数据
  • (数位dp) 算法竞赛入门到进阶 书本题集
  • (顺序)容器的好伴侣 --- 容器适配器
  • (贪心) LeetCode 45. 跳跃游戏 II
  • (转)清华学霸演讲稿:永远不要说你已经尽力了