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

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

JDK 动态代理 vs CGLIB 动态代理

1. 核心区别概览

特性JDK 动态代理CGLIB 动态代理
实现原理反射机制字节码生成(ASM)
代理对象实现接口继承目标类
代理目标只能代理接口可以代理类和接口
性能创建快,执行稍慢创建慢,执行快
JDK 版本JDK 原生支持需要第三方库
限制不能代理 final 类不能代理 final 类和方法
Spring 默认优先使用无接口时使用

2. 实现原理对比

2.1 JDK 动态代理原理

┌─────────────────────────────────────────┐ │ JDK 动态代理工作流程 │ ├─────────────────────────────────────────┤ │ 1. Proxy.newProxyInstance() │ │ ↓ │ │ 2. 运行时生成代理类(实现接口) │ │ ↓ │ │ 3. 所有方法调用 → InvocationHandler │ │ ↓ │ │ 4. 通过反射调用目标方法 │ └─────────────────────────────────────────┘

生成的代理类结构

// JDK 动态生成的代理类(简化版)publicfinalclass$Proxy0extendsProxyimplementsUserService{public$Proxy0(InvocationHandlerh){super(h);}publicvoidaddUser(Stringname){try{// 调用 InvocationHandlersuper.h.invoke(this,m3,newObject[]{name});}catch(Throwablee){// 异常处理}}privatestaticMethodm3;static{m3=Class.forName("UserService").getMethod("addUser",String.class);}}

2.2 CGLIB 动态代理原理

┌─────────────────────────────────────────┐ │ CGLIB 动态代理工作流程 │ ├─────────────────────────────────────────┤ │ 1. Enhancer.create() │ │ ↓ │ │ 2. 字节码生成子类(继承目标类) │ │ ↓ │ │ 3. 重写所有非 final 方法 │ │ ↓ │ │ 4. 方法调用 → MethodInterceptor │ │ ↓ │ │ 5. 直接调用目标方法(FastClass) │ └─────────────────────────────────────────┘

生成的代理类结构

// CGLIB 动态生成的代理类(简化版)publicclassUserService$$EnhancerByCGLIB$$12345678extendsUserService{privateMethodInterceptorinterceptor;publicvoidaddUser(Stringname){MethodInterceptorvar10000=this.interceptor;if(var10000==null){super.addUser(name);}else{// 调用拦截器var10000.intercept(this,CGLIB$addUser$0$Method,newObject[]{name},CGLIB$addUser$0$Proxy);}}// 保存原始方法引用finalvoidCGLIB$addUser$0(Stringname){super.addUser(name);}}

3. 代码实现对比

3.1 JDK 动态代理示例

importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Method;importjava.lang.reflect.Proxy;// 接口(必须)interfaceUserService{voidaddUser(Stringname);voiddeleteUser(Stringname);}// 实现类classUserServiceImplimplementsUserService{publicvoidaddUser(Stringname){System.out.println("添加用户: "+name);}publicvoiddeleteUser(Stringname){System.out.println("删除用户: "+name);}}// JDK 动态代理publicclassJdkProxyDemo{publicstaticvoidmain(String[]args){UserServicetarget=newUserServiceImpl();// 创建代理UserServiceproxy=(UserService)Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),newInvocationHandler(){@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{System.out.println("[JDK] 前置增强");Objectresult=method.invoke(target,args);System.out.println("[JDK] 后置增强");returnresult;}});proxy.addUser("张三");}}

3.2 CGLIB 动态代理示例

importnet.sf.cglib.proxy.Enhancer;importnet.sf.cglib.proxy.MethodInterceptor;importnet.sf.cglib.proxy.MethodProxy;// 目标类(不需要接口)classUserService{publicvoidaddUser(Stringname){System.out.println("添加用户: "+name);}publicvoiddeleteUser(Stringname){System.out.println("删除用户: "+name);}}// CGLIB 动态代理publicclassCglibProxyDemo{publicstaticvoidmain(String[]args){Enhancerenhancer=newEnhancer();enhancer.setSuperclass(UserService.class);// 设置父类enhancer.setCallback(newMethodInterceptor(){@OverridepublicObjectintercept(Objectobj,Methodmethod,Object[]args,MethodProxyproxy)throwsThrowable{System.out.println("[CGLIB] 前置增强");Objectresult=proxy.invokeSuper(obj,args);// 调用父类方法System.out.println("[CGLIB] 后置增强");returnresult;}});UserServiceproxy=(UserService)enhancer.create();proxy.addUser("张三");}}

4. 详细对比

4.1 代理目标

// ✅ JDK:只能代理接口interfaceInterfaceA{}classClassAimplementsInterfaceA{}InterfaceAjdkProxy=(InterfaceA)Proxy.newProxyInstance(ClassA.class.getClassLoader(),ClassA.class.getInterfaces(),// 必须是接口handler);// ❌ JDK:不能直接代理类ClassAjdkProxy2=(ClassA)Proxy.newProxyInstance(ClassA.class.getClassLoader(),ClassA.class.getInterfaces(),// 空数组或接口handler);// 运行时错误// ✅ CGLIB:可以代理类Enhancerenhancer=newEnhancer();enhancer.setSuperclass(ClassA.class);// 设置父类ClassAcglibProxy=(ClassA)enhancer.create();// ✅ CGLIB:也可以代理接口Enhancerenhancer2=newEnhancer();enhancer2.setSuperclass(InterfaceA.class);InterfaceAcglibProxy2=(InterfaceA)enhancer2.create();

4.2 性能对比

publicclassPerformanceTest{publicstaticvoidmain(String[]args){// 测试创建代理的性能longstart=System.currentTimeMillis();for(inti=0;i<10000;i++){// JDK 代理创建Proxy.newProxyInstance(...);}System.out.println("JDK 创建耗时: "+(System.currentTimeMillis()-start));// CGLIB 代理创建start=System.currentTimeMillis();for(inti=0;i<10000;i++){Enhancerenhancer=newEnhancer();enhancer.create();}System.out.println("CGLIB 创建耗时: "+(System.currentTimeMillis()-start));// 测试方法调用性能// JDK: 反射调用,稍慢// CGLIB: FastClass 机制,更快}}

性能特点

  • 创建代理:JDK > CGLIB(JDK 更快)
  • 方法调用:CGLIB > JDK(CGLIB 更快)
  • 内存占用:CGLIB > JDK(CGLIB 需要生成更多类)

4.3 限制对比

// JDK 限制finalclassFinalClass{}// final 类// ❌ JDK 不能代理(因为没有接口)// CGLIB 限制classMyClass{publicfinalvoidfinalMethod(){}// final 方法}Enhancerenhancer=newEnhancer();enhancer.setSuperclass(MyClass.class);MyClassproxy=(MyClass)enhancer.create();proxy.finalMethod();// ❌ 不会被拦截,直接调用原方法

5. Spring 中的选择策略

5.1 Spring AOP 代理选择

@Configuration@EnableAspectJAutoProxy(proxyTargetClass=false)// 默认 falsepublicclassAopConfig{// proxyTargetClass = false: 优先使用 JDK 动态代理// proxyTargetClass = true: 强制使用 CGLIB}

Spring 的选择逻辑

┌─────────────────────────────────────┐ │ Spring AOP 代理选择策略 │ ├─────────────────────────────────────┤ │ 目标类有接口? │ │ ├─ 是 → 使用 JDK 动态代理 │ │ └─ 否 → 使用 CGLIB │ │ │ │ proxyTargetClass = true? │ │ └─ 是 → 强制使用 CGLIB │ └─────────────────────────────────────┘

5.2 实际示例

// 场景 1:有接口,使用 JDK 代理interfaceOrderService{voidcreateOrder(StringorderId);}@ServiceclassOrderServiceImplimplementsOrderService{publicvoidcreateOrder(StringorderId){System.out.println("创建订单: "+orderId);}}// Spring 会使用 JDK 动态代理OrderServiceproxy=context.getBean(OrderService.class);System.out.println(proxy.getClass().getName());// 输出: com.sun.proxy.$Proxy123// 场景 2:无接口,使用 CGLIB@ServiceclassPaymentService{publicvoidpay(StringorderId){System.out.println("支付订单: "+orderId);}}// Spring 会使用 CGLIBPaymentServiceproxy=context.getBean(PaymentService.class);System.out.println(proxy.getClass().getName());// 输出: com.example.PaymentService$$EnhancerBySpringCGLIB$$456

6. 使用建议

6.1 选择 JDK 动态代理的场景

// ✅ 推荐:目标类有接口interfaceRepository{voidsave(Objectentity);}classUserRepositoryimplementsRepository{publicvoidsave(Objectentity){// 实现}}// ✅ 推荐:需要代理多个接口interfaceServiceA{}interfaceServiceB{}classMultiServiceimplementsServiceA,ServiceB{}// ✅ 推荐:关注创建性能// 需要频繁创建代理对象时

6.2 选择 CGLIB 的场景

// ✅ 推荐:目标类没有接口classUtilityService{publicvoiddoSomething(){// 实现}}// ✅ 推荐:需要代理类的方法(非接口方法)classBaseService{publicvoidcommonMethod(){}// 不在接口中}// ✅ 推荐:关注方法调用性能// 代理对象会被频繁调用时

7. 常见问题

7.1 循环依赖问题

@ServiceclassServiceA{@AutowiredprivateServiceBserviceB;}@ServiceclassServiceB{@AutowiredprivateServiceAserviceA;}// 使用 CGLIB 可以解决循环依赖// 因为 CGLIB 代理的是类,可以注入代理对象

7.2 类型转换问题

// JDK 代理interfaceMyInterface{}classMyClassimplementsMyInterface{}MyInterfaceproxy=(MyInterface)Proxy.newProxyInstance(...);// ✅ 可以转换为接口类型// MyClass obj = (MyClass) proxy; // ❌ 不能转换为实现类// CGLIB 代理MyClassproxy=(MyClass)enhancer.create();// ✅ 可以转换为类类型// MyInterface obj = (MyInterface) proxy; // ✅ 也可以转换为接口

8. 总结

维度JDK 动态代理CGLIB 动态代理
核心机制反射字节码生成
代理对象实现接口继承类
适用场景有接口的类无接口的类
创建性能
调用性能
Spring 默认优先使用无接口时使用
final 限制不能代理 final 类不能代理 final 方法
依赖JDK 原生cglib 库

选择建议

  • 有接口 → 优先 JDK 动态代理
  • 无接口 → 使用 CGLIB
  • 关注创建性能 → JDK 动态代理
  • 关注调用性能 → CGLIB
  • Spring 项目 → 让框架自动选择

理解两者的区别有助于在实际项目中做出合适的选择!

http://www.jsqmd.com/news/394871/

相关文章:

  • Java 中的 hashCode 和 equals 方法之间有什么关系?
  • g2o中信息矩阵(Information Matrix)的理解
  • 如何在大数据领域使用Hive进行数据可视化
  • 什么是 Java 中的动态代理?
  • Java 中 hashCode 和 equals 方法是什么?它们与 == 操作符有什么区别?
  • 《计算机是怎样跑起来的》————让写算法跟呼吸一样简单
  • 购物卡回收的三种热门方法整理 - 京回收小程序
  • ChatPPT Nano Banana Pro · Magic模式深度解析 ——重新定义“所想即所得”的PPT智能编辑
  • ARM Cortex-A7(IMX6ULL)嵌入式裸机开发指南:从点灯到中断 - 实践
  • 大润发购物卡回收靠谱的3个主流渠道 - 京回收小程序
  • 天猫超市购物卡回收常见三种方法及流程解析 - 京回收小程序
  • 最近在调试西门子808D数控系统的机械手刀库,整个过程虽然有点复杂,但还挺有意思的。今天就来分享一下我的调试经验,顺便贴点代码,希望能帮到有需要的朋友
  • 镜像孪生驱动的视频孪生升级版水利电力三维态势控制中枢白皮书---依托矩阵视频融合架构、统一三维坐标基准构建技术、动态误差修正模型与风险交汇时间解算算法形成的空间级前向布控平台-
  • 2026年公司起名机构推荐榜单:十大专业品牌深度测评,企业选型必看 - 博客万
  • 视频孪生之上,是镜像孪生镜像视界三维空间控制作战体系---基于镜像视界(浙江)科技有限公司矩阵视频融合、Pixel-to-3D 反演引擎、三维轨迹建模体系与趋势级风险推演算法构建的全域主动压制平
  • 从春晚舞台到万家灯火:菁彩Vivid三度携手央视频,以沉浸体验点亮中国年 - 博客万
  • 6大方法禁止win11自动更新
  • 进口维生素d3十大品牌揭晓,维生素d3哪个牌子成分安全?复配K2,锁钙护血管更安心 - 博客万
  • 免费招聘平台TOP榜盘点,前三名免费查看简历 - 博客万
  • 目前最靠谱的招聘网站?2026权威测评与真实口碑 - 博客万
  • 【Docker高级篇】吃透Docker CI/CD集成:从代码提交到镜像部署,一步到位不踩坑
  • 【Docker高级篇】吃透容器编排:Swarm vs K8s 核心差异,为后续K8s学习打牢基础
  • 【Docker高级篇】新手也能懂的应用安全:为什么不能用root?镜像怎么扫漏洞?
  • 大数据架构数据流水线:从采集到分析的完整设计
  • 基于博途1200PLC + HMI病床呼叫控制系统仿真探索
  • A2UI协议,打破Agent交互壁垒,让智能系统自主“搭建”界面 - 指南
  • YOLO26涨点改进 | 独家创新,注意力改进篇 | AAAI 2025 | YOLO26引入 DRM 防御优化模块,进行特征优化/特征增强,助力目标检测、图像分类、图像分割有效涨点
  • dokuwikiAPI 探索器
  • 哨兵机制(sentinel)的原理
  • Redis客观下线