jdk动态代理和cglib代理区别

本文介绍了JDK动态代理和CGLIB代理的区别,包括它们的工作原理、适用场景及性能对比。同时探讨了expose-proxy配置的作用,以及如何解决目标对象内部自我调用时切面增强的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JDK动态代理:目标对象实现了接口,默认使用此代理,通过在运行期间创建一个接口的实现类来完成对目标对象的代理

cglib代理:针对类实现代理,在运行期间生成代理对象是针对目标对象扩展的子类,所以此时要是目标对象有final方法,就不能重写覆盖了,底层依靠ASM(开源的java字节码编辑类库)操作字节码实现的,性能比JDK强

如果非要使用cglib代理,可以设置proxy-target-class="true",但此时需要考虑两个问题:

 1:无法通知(advise)final方法,因为他们不能重写覆盖

 2:需要引入cglib二进制发行包


expose-proxy:有时候目标对象内部的自我调用将无法实现切面中的增强,如下示例:

public interface AService{

 public void a();

 public void b();

}


public class AServiceImpl implements AService{

 @Transaction(propagation=Propagation.REQUIRED) 

 public void a(){

    this.b();

  }

@Transaction(propagation=Propagation.REQUIRES_NEW) 

  public void b(){}

}

此处的设置this指向目标对象,直接调用b方法将无法执行事务增强,此时修改expose-proxy=“”true” ,并且修改   ((AService)AopContext.currentProxy()).b()即可

<think>我们被要求比较JDK动态代理Cglib动态代理区别。根据提供的引用材料,我们可以从多个维度进行对比。以下是详细的对比分析: 1. **实现原理**: - **JDK动态代理**:基于接口。它使用Java反射机制,在运行时生成一个实现了指定接口的代理类(通常命名为$ProxyN)。代理对象会拦截所有接口方法调用,并将调用转发给InvocationHandler对象处理。 - **Cglib动态代理**:基于继承。它通过操作字节码(使用ASM库)生成目标类的子类作为代理类。代理类重写了目标类的方法,在方法中插入拦截逻辑,因此可以代理没有实现接口的类。 2. **目标类要求**: - **JDK动态代理**:要求目标类必须实现至少一个接口(因为代理类会实现相同的接口)[^1][^3]。 - **Cglib动态代理**:要求目标类不能是final类,且目标方法不能是final(因为需要继承并重写方法)[^2]。 3. **性能对比**: - **生成代理速度**:JDK动态代理在生成代理对象时较快,因为它直接使用反射API;而Cglib在生成代理对象时较慢,因为它需要动态生成字节码[^2]。 - **方法调用速度**:JDK动态代理在方法调用时较慢(因为使用反射调用);Cglib动态代理在方法调用时较快(因为它直接调用子类方法,且可以被JIT优化)[^2][^3]。 - **内存占用**:JDK动态代理内存占用较低(生成的代理类较简单);Cglib动态代理内存占用较高(因为它生成了完整的子类)[^2]。 4. **使用场景**: - **JDK动态代理**:适用于目标对象已经实现了一个或多个接口的情况。例如,Spring AOP默认使用JDK动态代理(当目标对象实现了接口时)[^3]。 - **Cglib动态代理**:适用于目标对象没有实现接口的情况,或者需要代理类而不是接口。Spring AOP在目标对象没有实现接口时会自动切换到Cglib[^3]。 5. **依赖**: - **JDK动态代理**:是Java标准库的一部分(java.lang.reflect包),无需额外依赖。 - **Cglib动态代理**:需要引入第三方库(cglibasm)。 6. **优缺点总结**: - **JDK动态代理**: - 优点:原生支持,无额外依赖;生成代理较快。 - 缺点:只能代理接口;反射调用有性能开销。 - **Cglib动态代理**: - 优点:可以代理类;方法调用速度快。 - 缺点:需要额外依赖;不能代理final类或方法;生成代理较慢,且内存占用高。 ### 对比表格 | 对比维度 | JDK动态代理 | Cglib动态代理 | |------------------|--------------------------------------|----------------------------------------| | **实现原理** | 基于接口(生成接口实现类) | 基于继承(生成目标类的子类) | | **目标类要求** | 必须实现接口 | 不能是final类,方法不能是final | | **生成代理速度** | 较快(反射API) | 较慢(需生成字节码) | | **方法调用速度** | 较慢(反射调用) | 较快(直接调用,可被JIT优化) | | **内存占用** | 较低 | 较高(生成完整子类) | | **使用场景** | 代理有接口的目标对象 | 代理无接口的目标对象 | | **依赖** | JDK原生支持 | 需引入cglibasm库 | ### 总结建议 - 如果目标对象已经实现了接口,优先使用**JDK动态代理**,因为它更轻量且无额外依赖。 - 如果目标对象没有实现接口,或者需要更高的方法调用性能,则使用**Cglib动态代理**,尽管它会带来额外的依赖内存开销。 在Spring框架中,默认策略是:如果目标对象实现了接口,则使用JDK动态代理;否则使用Cglib。也可以通过配置强制使用Cglib代理(如`@EnableAspectJAutoProxy(proxyTargetClass=true)`)[^3]。 --- §§相关问题§§ 1. 在Spring框架中如何配置强制使用Cglib代理? 2. 如何通过代码示例演示JDK动态代理Cglib动态代理的创建过程? 3. 动态代理在Spring AOP中的具体应用场景有哪些?</think>### JDK动态代理CGLIB动态代理对比分析 #### 1. 实现原理对比 | **维度** | **JDK动态代理** | **CGLIB动态代理** | |------------------|--------------------------------------------------------------------------------|----------------------------------------------------------------------------------| | **代理机制** | 基于接口实现<br>运行时生成接口的代理类(`$Proxy0`格式) | 基于继承实现<br>运行时生成目标类的子类 | | **底层技术** | Java反射机制<br>(`java.lang.reflect.Proxy` + `InvocationHandler`) | ASM字节码操作库<br>(通过`Enhancer`生成子类字节码) | | **目标类要求** | 必须实现至少一个接口 | 目标类不能是final类<br>目标方法不能是final | > 💡 **本质差异**: > JDK代理是**接口契约型代理**(实现相同接口)[^4] > CGLIB是**继承扩展型代理**(生成目标类的子类)[^1] --- #### 2. 性能对比 | **指标** | JDK动态代理 | CGLIB动态代理 | |------------------|--------------------------------------|----------------------------------------| | **代理生成速度** | 较快<br>(直接调用反射API) | 较慢<br>(需动态生成字节码) | | **方法调用速度** | 较慢<br>(反射调用有性能损耗) | 较快<br>(直接方法调用,可被JIT优化) | | **内存占用** | 较低<br>(轻量级代理类) | 较高<br>(生成完整子类) | **性能曲线示例**: ```mermaid graph LR A[首次调用] -->|JDK| B[反射初始化开销] A -->|CGLIB| C[字节码生成开销] D[多次调用] -->|JDK| E[反射调用持续损耗] D -->|CGLIB| F[直接调用高效执行] ``` > 📊 **实测数据参考**: > 万次方法调用时,CGLIBJDK代理快约30%-50%[^2] --- #### 3. 使用限制对比 | **限制类型** | JDK动态代理 | CGLIB动态代理 | |--------------------|-------------------------------------------------|------------------------------------------------| | **类限制** | 无类限制 | 不能代理final类<br>(如`String`, `Integer`等) | | **方法限制** | 只能代理接口方法 | 不能代理final方法<br>(如`Object.getClass()`) | | **依赖要求** | JDK原生支持 | 需引入第三方库<br>(cglib + asm) | --- #### 4. 使用场景对比 | **场景** | 推荐方案 | 原因说明 | |--------------------------|-------------------|--------------------------------------------------------------------------| | Spring默认代理策略 | JDK动态代理 | 当目标类实现接口时优先使用[^3] | | 代理无接口的类 | CGLIB | 可代理普通POJO类 | | 高性能要求的核心业务 | CGLIB | 避免反射调用损耗 | | 简单功能扩展(如日志) | JDK动态代理 | 开发便捷,无需额外依赖 | | 需要代理final方法 | 两者均不支持 | 需考虑AspectJ编译时织入 | --- #### 5. 代码结构差异 **JDK代理核心组件**: ```java // 调用处理器 public class JdkHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) { // 前置增强 Object result = method.invoke(target, args); // 反射调用 // 后置增强 return result; } } // 创建代理 Service proxy = (Service) Proxy.newProxyInstance( loader, new Class[]{Service.class}, // 必须为接口 new JdkHandler(target) ); ``` **CGLIB核心组件**: ```java // 方法拦截器 public class CglibInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) { // 前置增强 Object result = proxy.invokeSuper(obj, args); // 直接调用父类方法 // 后置增强 return result; } } // 创建代理 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(TargetClass.class); // 设置目标类(非接口) enhancer.setCallback(new CglibInterceptor()); TargetClass proxy = (TargetClass) enhancer.create(); ``` > 🔑 **关键区别**: > CGLIB的`invokeSuper()`直接调用父类方法,避免了JDK的反射调用[^1] --- #### 6. 框架应用策略 在Spring AOP中的智能选择机制: ```mermaid graph TD A[创建代理] --> B{目标类实现接口?} B -->|是| C[使用JDK动态代理] B -->|否| D[使用CGLIB代理] C --> E[生成接口代理类] D --> F[生成子类代理] ``` 可通过配置强制使用CGLIB: ```java @EnableAspectJAutoProxy(proxyTargetClass = true) // Spring配置 ``` --- ### 总结建议 | **选择依据** | **推荐方案** | |------------------------|--------------| | 目标类已实现接口 | ✅ JDK代理 | | 目标类是普通类 | ✅ CGLIB | | 需要最高性能 | ✅ CGLIB | | 避免第三方依赖 | ✅ JDK代理 | | 代理final类/方法 | ❌ 均不支持 | > 💎 **最佳实践**: > - 在Spring生态中优先采用框架的自动代理机制 > - 独立开发时根据目标类结构性能需求选择 > - 高并发场景建议用CGLIB+缓存优化(避免重复生成代理类)[^2] ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值