spring 动态代理如何决定使用jdk代理和cglib(网易面试题)

本文探讨了Spring框架中AOP(面向切面编程)的两种代理机制:JDK动态代理与CGLIB代理。详细介绍了如何根据目标对象是否实现接口来选择不同的代理方式,并解释了如何强制使用CGLIB代理及其优缺点。

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

Spring1.2:
将事务代理工厂[TransactionProxyFactoryBean] 或 自动代理拦截器[BeanNameAutoProxyCreator]
的 proxyTargetClass 属性,设置为true,则使用CGLIB代理,此属性默认为false,使用JDK动态代理.
以下引用 Spring Framework reference 2.0.5:
Spring2.0:

Spring AOP部分使用JDK动态代理或者CGLIB来为目标对象创建代理。(建议尽量使用JDK的动态代理)

如果被代理的目标对象实现了至少一个接口,则会使用JDK动态代理。所有该目标类型实现的接口都将被代理。若该目标对象没有实现任何接口,则创建一个CGLIB代理。

如果你希望强制使用CGLIB代理,(例如:希望代理目标对象的所有方法,而不只是实现自接口的方法)那也可以。但是需要考虑以下问题:

无法通知(advise)Final 方法,因为他们不能被覆写。 
你需要将CGLIB 2二进制发行包放在classpath下面,与之相较JDK本身就提供了动态代理 
强制使用CGLIB代理需要将 |aop:config| 的 proxy-target-class 属性设为true:

|aop:config proxy-target-class="true"|
...
|/aop:config|

当需要使用CGLIB代理和@AspectJ自动代理支持,请按照如下的方式设置 |aop:aspectj-autoproxy| 的 proxy-target-class 属性:

|aop:aspectj-autoproxy proxy-target-class="true"/|

而实际使用的过程中才会发现细节问题的差别,The devil is in the detail.JDK动态代理:其代理对象必须是某个接口的实现,它是通过在运行期间创建一个接口的实现类来完成对目标对象的代理。
CGLIB代理:实现原理类似于JDK动态代理,只是它在运行期间生成的代理对象是针对目标类扩展的子类。CGLIB是高效的代码生成包,底层是依靠ASM(开源的java字节码编辑类库)操作字节码实现的,性能比JDK强。
Spring是依靠什么来判断采用哪种代理策略来生成AOP代理呢?以下代码就是Spring的判断逻辑  

    //org.springframework.aop.framework.DefaultAopProxyFactory   
    
//参数AdvisedSupport 是Spring AOP配置相关类   
    public AopProxy createAopProxy(AdvisedSupport advisedSupport)   
            
throws AopConfigException {   
        
//在此判断使用JDK动态代理还是CGLIB代理   
        if (advisedSupport.isOptimize() || advisedSupport.isProxyTargetClass()   
                
|| hasNoUserSuppliedProxyInterfaces(advisedSupport)) {   
            
if (!cglibAvailable) {   
                
throw new AopConfigException(   
                        
"Cannot proxy target class because CGLIB2 is not available. "  
                                
+ "Add CGLIB to the class path or specify proxy interfaces.");   
              }   
            
return CglibProxyFactory.createCglibProxy(advisedSupport);   
          } 
else {   
            
return new JdkDynamicAopProxy(advisedSupport);   
          }   
      }  

advisedSupport.isOptimize()与advisedSupport.isProxyTargetClass()默认返回都是false,所以在默认情况下目标对象有没有实现接口决定着Spring采取的策略,当然可以设置advisedSupport.isOptimize()或者advisedSupport.isProxyTargetClass()返回为true,这样无论目标对象有没有实现接口Spring都会选择使用CGLIB代理。

所以在默认情况下,如果一个目标对象如果实现了接口Spring则会选择JDK动态代理策略动态的创建一个接口实现类(动态代理类)来代理目标对象,可以通俗的理解这个动态代理类是目标对象的另外一个版本,所以这两者之间在强制转换的时候会抛出java.lang.ClassCastException。而所以在默认情况下,如果目标对象没有实现任何接口,Spring会选择CGLIB代理, 其生成的动态代理对象是目标类的子类。

上说的是默认情况下,也可以手动配置一些选项使Spring采用CGLIB代理。 
org.springframework.transaction.interceptor.TransactionProxyFactoryBean是org.springframework.aop.framework. ProxyConfig的子类,所以可以参照ProxyConfig里的一些设置如下所示,将optimize和proxyTargetClass任意一个设置为true都可以强制Spring采用CGLIB代理。

如果当需要使用CGLIB代理和@AspectJ自动代理支持,请按照如下的方式设置 |aop:aspectj-autoproxy| 的 proxy-target-class 属性: 
|aop:aspectj-autoproxy proxy-target-class="true"/|

这样使用CGLIB代理也就不会出现前面提到的ClassCastException问题了,也可以在性能上有所提高,关键是对于代理对象是否继承接口可以统一使用。

转载:https://blog.youkuaiyun.com/samjustin1/article/details/52382972

<think>好的,我现在需要回答用户关于CGLIB动态代理JDK动态代理的区别及使用场景的问题,这是一个常见的Java面试题。首先,我得回忆一下这两个动态代理的基本概念它们之间的主要差异。 首先,JDK动态代理是基于接口的,也就是说它只能为实现了接口的类生成代理。这是因为它通过反射机制调用目标对象的方法,而反射需要接口的支持。而CGLIB动态代理则是通过继承目标类来实现代理,因此它不需要目标类实现接口,这对于那些没有接口的类来说非常有用。不过,使用CGLIB的时候需要注意,如果目标类的方法是final的,那么就无法被代理,因为CGLIB无法覆盖final方法。 接下来是性能方面的问题。我记得以前有资料提到,在JDK 8之前,CGLIB的性能通常比JDK动态代理要好,尤其是在代理方法调用频繁的情况下。这是因为CGLIB生成的代理类直接继承目标类,方法调用可能更直接,而JDK动态代理则通过反射调用,可能会有额外的开销。不过,随着JDK版本的更新,尤其是JDK 8及以后,反射的性能有所提升,可能两者的差距变小了,甚至在某些情况下JDK代理更快。不过具体的数据可能需要查阅最新的基准测试结果,这点需要确认。 然后是生成代理的方式。JDK动态代理使用Java原生的Proxy类InvocationHandler接口,代理对象是在运行时动态生成的。而CGLIB使用ASM库直接操作字节码,生成目标类的子类作为代理类。这也意味着CGLIB在生成代理时可能会比JDK动态代理慢一些,尤其是在首次生成时,但生成的代理类在方法调用时可能更快。 使用场景方面,如果目标对象已经实现了接口,那么JDK动态代理是一个自然的选择,因为它不需要引入额外的库。而如果目标类没有实现接口,或者需要代理没有接口的第三方类,CGLIB就是更好的选择。不过要注意,由于CGLIB通过继承实现代理,所以不能代理final类或final方法。 还有,依赖方面的问题。JDK动态代理是Java标准库的一部分,不需要额外依赖。而CGLIB是一个第三方库,使用时需要添加到项目的依赖中,比如通过Maven或Gradle引入。 另外,异常处理也是一个区别。JDK动态代理在调用方法时,如果方法声明了受检异常,代理类需要处理这些异常,或者声明抛出。而CGLIB在这方面可能更灵活,因为它生成的是子类,可以覆盖父类的方法,异常处理的方式可能有所不同。 需要确保提到的点准确无误,特别是性能部分,可能存在版本差异,所以最好引用可靠的资料。比如,Spring框架在选择代理方式时,会根据目标类是否实现接口来决定使用JDK还是CGLIB,这也是一个实际应用的例子。 现在,可能还需要考虑两者的实现原理的详细对比,比如JDK代理是基于接口,CGLIB基于继承,这导致它们在设计上的不同限制。此外,CGLIB不能代理private方法,因为子类无法访问父类的私有方法,而JDK代理则是通过接口方法调用来实现的,所以如果接口中没有定义的方法,也无法被代理。 最后,使用场景需要具体举例,比如在Spring AOP中,默认情况下如果目标对象实现了接口,就使用JDK动态代理,否则使用CGLIB。但在配置中也可以强制使用CGLIB,即使有接口存在,这时候可能需要考虑性能维护性的权衡。 总结一下,主要区别包括实现方式(接口 vs 继承)、性能差异、依赖关系、对目标类的要求(是否有接口、是否final)以及使用场景的选择。需要将这些点有条理地组织起来,用简洁明了的语言表达,同时准备好相关问题,比如性能优化、如何选择等。</think>### CGLIB动态代理JDK动态代理的区别及使用场景 #### 1. **实现原理** - **JDK动态代理**:基于接口实现,通过反射机制生成代理类。要求目标类必须实现至少一个接口,代理对象会实现相同的接口并重写方法。 ```java // JDK代理示例 public interface UserService { void save(); } public class UserServiceImpl implements UserService { public void save() { /* 实现 */ } } // 代理处理器 public class MyInvocationHandler implements InvocationHandler { private Object target; public MyInvocationHandler(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(target, args); } } // 生成代理对象 UserService proxy = (UserService) Proxy.newProxyInstance( loader, new Class[]{UserService.class}, new MyInvocationHandler(target) ); ``` - **CGLIB动态代理**:基于继承实现,通过ASM字节码框架生成目标类的子类作为代理类。不要求目标类实现接口,但无法代理`final`类或`final`方法。 ```java // CGLIB代理示例 public class UserService { public void save() { /* 实现 */ } } // 方法拦截器 public class MyMethodInterceptor implements MethodInterceptor { public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { return proxy.invokeSuper(obj, args); } } // 生成代理对象 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserService.class); enhancer.setCallback(new MyMethodInterceptor()); UserService proxy = (UserService) enhancer.create(); ``` #### 2. **性能对比** - **生成代理速度**:JDK动态代理在生成代理类时更快(直接使用Java原生API),而CGLIB首次生成较慢(需操作字节码)。 - **方法调用速度**:在JDK 8之前,CGLIB代理的方法调用性能优于JDK代理[^1]。但随着JDK反射优化(如`MethodHandle`),二者差距缩小,甚至JDK代理在某些场景下更快[^2]。 #### 3. **依赖与兼容性** - **JDK代理**:无需额外依赖,仅依赖Java标准库。 - **CGLIB**:需引入第三方库(如`cglib`或Spring的`spring-core`)。 #### 4. **使用场景** - **JDK动态代理适用场景**: - 目标类已实现接口。 - 需要轻量级代理且避免引入第三方库。 - 例如:Spring AOP默认对接口实现类使用JDK代理[^3]。 - **CGLIB适用场景**: - 目标类未实现接口。 - 需要代理`static`方法或非`final`方法。 - 例如:Spring AOP对未实现接口的类默认使用CGLIB[^3]。 #### 5. **限制与注意事项** - **JDK代理**:无法代理未在接口中声明的方法。 - **CGLIB**: - 无法代理`final`类或`final`方法。 - 代理类会重写目标类的方法,可能导致`equals()`或`hashCode()`行为异常。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值