Spring AOP中的JDK和CGLib动态代理哪个效率更高?

Spring AOP中的JDK和CGLib动态代理哪个效率更高?一、背景今天有小伙伴面试的时候被问到:SpringAOP中JDK和CGLib动态代理哪个效率更高?二、基本概念首先,我们知道SpringAOP的底层实现有两种方式:一种是JDK动态代理,另一种是CGLib的方式。自Java1.3以后,Java提供了动态代理技术,允许开发者在运行期创建接口的代理实例,后来这项技术被用到了Spring的很多地方。JDK动态代理主要涉及…

大家好,又见面了,我是你们的朋友全栈君。

一、背景

今天有小伙伴面试的时候被问到:Spring AOP中JDK 和 CGLib动态代理哪个效率更高?

二、基本概念

首先,我们知道Spring AOP的底层实现有两种方式:一种是JDK动态代理,另一种是CGLib的方式。

自Java 1.3以后,Java提供了动态代理技术,允许开发者在运行期创建接口的代理实例,后来这项技术被用到了Spring的很多地方。

JDK动态代理主要涉及java.lang.reflect包下边的两个类:Proxy和InvocationHandler。其中,InvocationHandler是一个接口,可以通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态地将横切逻辑和业务逻辑贬值在一起。

JDK动态代理的话,他有一个限制,就是它只能为接口创建代理实例,而对于没有通过接口定义业务方法的类,如何创建动态代理实例哪?答案就是CGLib。

CGLib采用底层的字节码技术,全称是:Code Generation Library,CGLib可以为一个类创建一个子类,在子类中采用方法拦截的技术拦截所有父类方法的调用并顺势织入横切逻辑。

三、JDK 和 CGLib动态代理区别

1、JDK动态代理具体实现原理:

  • 通过实现InvocationHandlet接口创建自己的调用处理器;

  • 通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理;

  • 通过反射机制获取动态代理类的构造函数,其唯一参数类型就是调用处理器接口类型;

  • 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数参入;

JDK动态代理是面向接口的代理模式,如果被代理目标没有接口那么Spring也无能为力,Spring通过Java的反射机制生产被代理接口的新的匿名实现类,重写了其中AOP的增强方法。

2、CGLib动态代理:

CGLib是一个强大、高性能的Code生产类库,可以实现运行期动态扩展java类,Spring在运行期间通过 CGlib继承要被动态代理的类,重写父类的方法,实现AOP面向切面编程呢。

3、两者对比:

  • JDK动态代理是面向接口的。

  • CGLib动态代理是通过字节码底层继承要代理类来实现(如果被代理类被final关键字所修饰,那么抱歉会失败)。

4、使用注意:

  • 如果要被代理的对象是个实现类,那么Spring会使用JDK动态代理来完成操作(Spirng默认采用JDK动态代理实现机制);

  • 如果要被代理的对象不是个实现类那么,Spring会强制使用CGLib来实现动态代理。

四、JDK 和 CGLib动态代理性能对比-教科书上的描述

我们不管是看书还是看文章亦或是我那个上搜索参考答案,可能很多时候,都可以找到如下的回答:

关于两者之间的性能的话,JDK动态代理所创建的代理对象,在以前的JDK版本中,性能并不是很高,虽然在高版本中JDK动态代理对象的性能得到了很大的提升,但是他也并不是适用于所有的场景。主要体现在如下的两个指标中:

1、CGLib所创建的动态代理对象在实际运行时候的性能要比JDK动态代理高不少,有研究表明,大概要高10倍;

2、但是CGLib在创建对象的时候所花费的时间却比JDK动态代理要多很多,有研究表明,大概有8倍的差距;

3、因此,对于singleton的代理对象或者具有实例池的代理,因为无需频繁的创建代理对象,所以比较适合采用CGLib动态代理,反正,则比较适用JDK动态代理。

结果是不是如上边1、2、3条描述的那样哪?下边我们做一些小实验分析一下!

五、性能测试

1、首先有几个Java类

这里写图片描述

2、Target.java

package com.java.proxy.test;

public interface Target {

    int test(int i);

}

3、TargetImpl.java

package com.java.proxy.test;

public class TargetImpl implements Target {

    @Override
    public int test(int i) {
        return i + 1;
    }
}

4、JdkDynamicProxyTest.java

package com.java.proxy.test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JdkDynamicProxyTest implements InvocationHandler {

    private Target target;

    private JdkDynamicProxyTest(Target target) {
        this.target = target;
    }

    public static Target newProxyInstance(Target target) {
        return (Target) Proxy.newProxyInstance(JdkDynamicProxyTest.class.getClassLoader(),
                new Class<?>[]{Target.class},
                new JdkDynamicProxyTest(target));

    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(target, args);
    }
}

5、CglibProxyTest.java

package com.java.proxy.test;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibProxyTest implements MethodInterceptor {

    private CglibProxyTest() {
    }

    public static <T extends Target> Target newProxyInstance(Class<T> targetInstanceClazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetInstanceClazz);
        enhancer.setCallback(new CglibProxyTest());
        return (Target) enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        return proxy.invokeSuper(obj, args);
    }

}

6、ProxyPerformanceTest.java

package com.java.proxy.test;

import java.util.LinkedHashMap;
import java.util.Map;

public class ProxyPerformanceTest {

    public static void main(String[] args) {
        //创建测试对象
        Target nativeTest = new TargetImpl();
        Target dynamicProxy = JdkDynamicProxyTest.newProxyInstance(nativeTest);
        Target cglibProxy = CglibProxyTest.newProxyInstance(TargetImpl.class);

        //预热一下
        int preRunCount = 10000;
        runWithoutMonitor(nativeTest, preRunCount);
        runWithoutMonitor(cglibProxy, preRunCount);
        runWithoutMonitor(dynamicProxy, preRunCount);

        //执行测试
        Map<String, Target> tests = new LinkedHashMap<String, Target>();
        tests.put("Native   ", nativeTest);
        tests.put("Dynamic  ", dynamicProxy);
        tests.put("Cglib    ", cglibProxy);
        int repeatCount = 3;
        int runCount = 1000000;
        runTest(repeatCount, runCount, tests);
        runCount = 50000000;
        runTest(repeatCount, runCount, tests);
    }

    private static void runTest(int repeatCount, int runCount, Map<String, Target> tests) {
        System.out.println(
                String.format("\n===== run test : [repeatCount=%s] [runCount=%s] [java.version=%s] =====",
                        repeatCount, runCount, System.getProperty("java.version")));
        for (int i = 0; i < repeatCount; i++) {
            System.out.println(String.format("\n--------- test : [%s] ---------", (i + 1)));
            for (String key : tests.keySet()) {
                runWithMonitor(tests.get(key), runCount, key);
            }
        }
    }

    private static void runWithoutMonitor(Target target, int runCount) {
        for (int i = 0; i < runCount; i++) {
            target.test(i);
        }
    }

    private static void runWithMonitor(Target target, int runCount, String tag) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < runCount; i++) {
            target.test(i);
        }
        long end = System.currentTimeMillis();
        System.out.println("[" + tag + "] Total Time:" + (end - start) + "ms");
    }
}

7、测试结果

(1)JDK 1.6

这里写图片描述
这里写图片描述

(2)JDK 1.7

这里写图片描述

这里写图片描述

(3)JDK 1.8

这里写图片描述

这里写图片描述

经过多次试验,可以看出平均情况下的话,JDK动态代理的运行速度已经逐渐提高了,在低版本的时候,运行的性能可能不如CGLib,但是在1.8版本中运行多次,基本都可以得到一致的测试结果,那就是JDK动态代理已经比CGLib动态代理快了!

但是JDK动态代理和CGLib动态代理的适用场景还是不一样的哈!

六、总结

最终的测试结果大致是这样的,在1.6和1.7的时候,JDK动态代理的速度要比CGLib动态代理的速度要慢,但是并没有教科书上的10倍差距,在JDK1.8的时候,JDK动态代理的速度已经比CGLib动态代理的速度快很多了,希望小伙伴在遇到这个问题的时候能够有的放矢!

Spring AOP中的JDK和CGLib动态代理关于这个知识点很重要,关于两者之间性能的对比经过测试实验已经有了一个初步的结果,以后再有人问你Spring AOP,不要简单的说JDK动态代理和CGLib这两个了,是时候的可以抛出来对两者之间区别的理解,是有加分的哦!

参考文章:

1、https://blog.csdn.net/qq1723205668/article/details/56481476
2、https://blog.csdn.net/xiangbq/article/details/49794335
在这里插入图片描述

【视频福利】2T免费学习视频,搜索或扫描上述二维码关注微信公众号:Java后端技术(ID: JavaITWork)回复:1024,即可免费获取!内含SSM、Spring全家桶、微服务、MySQL、MyCat、集群、分布式、中间件、Linux、网络、多线程,Jenkins、Nexus、Docker、ELK等等免费学习视频,持续更新!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/133679.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 大数据剖析热点新闻:996、巴黎圣母院、奔驰维权为什么成为本周热搜

    大数据剖析热点新闻:996、巴黎圣母院、奔驰维权为什么成为本周热搜

  • 香农编码的matlab实现实验总结_香农编码C语言

    香农编码的matlab实现实验总结_香农编码C语言中南大学《信息论与编码》实验报告题目信源编码实验指导教师学院专业班级姓名学号日期目录一、香农编码…………………………………………..3实验目的………………………………………………………………………3实验要求……………..

  • SplitContainer的使用

    SplitContainer的使用作用:(Splitter的简化)使用 SplitContainer 控件可分隔容器(如 Form)的显示区域,并使用户可以调整已添加到 SplitContainer 面板中的控件的大小。当用户将鼠标指针移到拆分器上时,光标将发生变化,以指示可以调整 SplitContainer 控件内部的控件的大小。SplitContainer 也使设计时的控件放置更容易。例如,若要创建一个与Win

  • memwatch使用[通俗易懂]

    memwatch使用[通俗易懂]一、简介memwatch可以跟踪程序中的内存泄漏和错误,能检测双重释放(double-free)、错误释放(erroneousfree)、没有释放的内存(unfreedmemory)、溢出(Overflow)、下溢(Underflow)等。下载地址:http://www.linkdata.se/sourcecode/memwatch/解压后,得到源码memwa

  • Android常用控件

    Android常用控件TextView显示文本<TextViewandroid:id="@+id/text_view"android:layout_width="match_pa

  • 独立成分分析(Independent Component Analysis,ICA)原理及代码实现

    独立成分分析(Independent Component Analysis,ICA)原理及代码实现过程监控中会用到很多中方法,如主成分分析(PCA)、慢特征分析(SFA)、概率MVA方法或独立成分分析(ICA)等为主流算法。其中PCA主要多用于降维及特征提取,且只对正太分布(高斯分布)数据样本有效;SFA被用来学习过程监控的时间相关表示,SFA不仅可以通过监测稳态分布来检测与运行条件的偏差,还可以根据时间分布来识别过程的动态异常,多用于分类分析;概率MVA方法,多以解决动力学、时变、非线性等问题。今天要介绍的是独立成分分析(ICA),由浅入深,细细道来。此外文末还附有ICA可实现的代码哟~不要错过

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号