Spring中JDK动态代理和Cglib代理的区别

    Spring的AOP基于代理模式,并且它即使用到了JDK动态代理也使用了Cglib代理。

    如下代码是springAOP中默认的代理实现类DefaultAopProxyFactory,其中创建代理的方法createAopProxy,在这个方法中Spring通过判断targetClass是否有实现接口来确定使用哪一种代理,如果实现了接口则使用JDK动态代理,如果没有实现接口则使用Cglib代理。同时JdkDynamicAopProxy为JDK动态代理的生成类,ObjenesisCglibAopProxy可以生成Cglib代理。

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
    public DefaultAopProxyFactory() {
    }

	//创建代理
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
            return new JdkDynamicAopProxy(config);//生成JDK动态代理
        } else {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
            } else {//ObjenesisCglibAopProxy生成Cglib代理
                return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));
            }
        }
    }

    private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
        Class<?>[] ifcs = config.getProxiedInterfaces();
        return ifcs.length == 0 || ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0]);
    }
}

    从上面代码可以发现当需要被代理的类实现了接口的时候Spring会使用动态代理,当被代理的类没有接口实现的时候使用的是Cglib,我们也可以强制使用CGlib,在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>。

    性能问题:由于Cglib代理是利用ASM字节码生成框架在内存中生成一个需要被代理类的子类完成代理,而JDK动态代理是利用反射原理完成动态代理,所以Cglib创建的动态代理对象性能比JDk动态代理动态创建出来的代理对象新能要好的多,但是对象创建的速度比JDk动态代理要慢,所以,当Spring使用的是单例情况下可以选用Cglib代理,反之使用JDK动态代理更加合适。同时还有一个问题,被final修饰的类只能使用JDK动态代理,因为被final修饰的类不能被继承,而Cglib则是利用的继承原理实现代理的。

关于动态代理请参考:https://my.oschina.net/zicheng/blog/1920396

转载于:https://my.oschina.net/zicheng/blog/1920892

### Spring AOP 中 JDK 动态代理CGLIB区别 #### 区别概述 JDK 动态代理主要依赖于 Java 内置的 `java.lang.reflect.Proxy` 类来创建代理对象,而这些代理对象必须实现一个或多个接口。这种方式适用于目标类实现了至少一个接口的情况[^1]。 相比之下,CGLIB 是一种高性能的字节码生成库,它可以在运行时扩展 Java 类并定义新的方法。对于那些没有提供任何接口的目标类来说,CGLIB 提供了一种有效的解决方案。通过继承原始类并重写其非最终的方法,CGLIB 能够实现代理功能[^2]。 #### 实现原理 - **JDK 动态代理** 当使用 JDK 动态代理时,实际是在内存中构建了一个实现了相同接口的新类实例,并在这个新实例上调用指定的方法前/后执行额外逻辑。此过程利用了反射机制,因此性能上可能会稍逊一些,尤其是在频繁调用的情况下[^3]。 ```java // 创建 InvocationHandler 来处理方法调用 InvocationHandler handler = new MyInvocationHandler(target); // 获取被代理对象所实现的所有接口 Class<?>[] interfaces = target.getClass().getInterfaces(); // 返回由给定加载器接口数组构造的一个代理类的新实例 return Proxy.newProxyInstance(loader, interfaces, handler); ``` - **CGLIB** 对于未实现接口的对象,则采用 CGLIB 库来进行代理操作。CGLIB 可以直接子类化现有类,并覆盖其中的方法以便插入自定义行为。由于不需要借助接口,所以适用范围更广;不过需要注意的是,如果原类中有 final 方法则无法被覆写,也就意味着这部分不会受到 AOP 切面的影响[^4]。 ```java Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Target.class); // 设置要代理的父类 enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // 执行前置通知或其他业务逻辑... Object result = proxy.invokeSuper(obj, args); // 调用父类方法 // 执行后置通知或其他业务逻辑... return result; } }); Target proxy = (Target) enhancer.create(); // 创建代理对象 ``` #### 性能对比 通常情况下,CGLIB 在某些特定场景下可能比 JDK 动态代理表现得更好,特别是在大量实例化的环境中。然而,在大多数应用场景里两者之间的差异并不明显,除非有非常严格的性能需求才需考虑这一点。另外值得注意的是,随着 JVM 编译优化的进步,这种差距正在逐渐缩小。 #### 使用场景建议 - 如果应用程序中的服务层组件都遵循良好的面向接口编程原则(即每个具体的服务都有对应的接口),那么推荐优先选用 JDK 动态代理方式。 - 若遇到确实存在不带接口的具体类需要加入到 AOP 处理链路之中,此时应该选择 CGLIB 方案作为替代方案之一。 - 还有一点很重要,当涉及到对私有成员变量的操作或是希望避免因修改原有代码结构带来的风险时,也可以倾向于使用 CGLIB 技术。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值