亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Spring AOP中的JDK和CGLib動(dòng)態(tài)代理哪個(gè)效率更高?

 更新時(shí)間:2019年03月01日 10:42:20   作者:徐劉根  
今天小編就為大家分享一篇關(guān)于Spring AOP中的JDK和CGLib動(dòng)態(tài)代理哪個(gè)效率更高?,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧

一、背景

今天有小伙伴面試的時(shí)候被問到:Spring AOP中JDK 和 CGLib動(dòng)態(tài)代理哪個(gè)效率更高?

二、基本概念

首先,我們知道Spring AOP的底層實(shí)現(xiàn)有兩種方式:一種是JDK動(dòng)態(tài)代理,另一種是CGLib的方式。

自Java 1.3以后,Java提供了動(dòng)態(tài)代理技術(shù),允許開發(fā)者在運(yùn)行期創(chuàng)建接口的代理實(shí)例,后來這項(xiàng)技術(shù)被用到了Spring的很多地方。

JDK動(dòng)態(tài)代理主要涉及java.lang.reflect包下邊的兩個(gè)類:Proxy和InvocationHandler。其中,InvocationHandler是一個(gè)接口,可以通過實(shí)現(xiàn)該接口定義橫切邏輯,并通過反射機(jī)制調(diào)用目標(biāo)類的代碼,動(dòng)態(tài)地將橫切邏輯和業(yè)務(wù)邏輯貶值在一起。

JDK動(dòng)態(tài)代理的話,他有一個(gè)限制,就是它只能為接口創(chuàng)建代理實(shí)例,而對(duì)于沒有通過接口定義業(yè)務(wù)方法的類,如何創(chuàng)建動(dòng)態(tài)代理實(shí)例哪?答案就是CGLib。

CGLib采用底層的字節(jié)碼技術(shù),全稱是:Code Generation Library,CGLib可以為一個(gè)類創(chuàng)建一個(gè)子類,在子類中采用方法攔截的技術(shù)攔截所有父類方法的調(diào)用并順勢織入橫切邏輯。

三、JDK 和 CGLib動(dòng)態(tài)代理區(qū)別

1、JDK動(dòng)態(tài)代理具體實(shí)現(xiàn)原理:

  • 通過實(shí)現(xiàn)InvocationHandlet接口創(chuàng)建自己的調(diào)用處理器;
  • 通過為Proxy類指定ClassLoader對(duì)象和一組interface來創(chuàng)建動(dòng)態(tài)代理;
  • 通過反射機(jī)制獲取動(dòng)態(tài)代理類的構(gòu)造函數(shù),其唯一參數(shù)類型就是調(diào)用處理器接口類型;
  • 通過構(gòu)造函數(shù)創(chuàng)建動(dòng)態(tài)代理類實(shí)例,構(gòu)造時(shí)調(diào)用處理器對(duì)象作為參數(shù)參入;

JDK動(dòng)態(tài)代理是面向接口的代理模式,如果被代理目標(biāo)沒有接口那么Spring也無能為力,Spring通過Java的反射機(jī)制生產(chǎn)被代理接口的新的匿名實(shí)現(xiàn)類,重寫了其中AOP的增強(qiáng)方法。

2、CGLib動(dòng)態(tài)代理:

CGLib是一個(gè)強(qiáng)大、高性能的Code生產(chǎn)類庫,可以實(shí)現(xiàn)運(yùn)行期動(dòng)態(tài)擴(kuò)展java類,Spring在運(yùn)行期間通過 CGlib繼承要被動(dòng)態(tài)代理的類,重寫父類的方法,實(shí)現(xiàn)AOP面向切面編程呢。

3、兩者對(duì)比:

  • JDK動(dòng)態(tài)代理是面向接口的。
  • CGLib動(dòng)態(tài)代理是通過字節(jié)碼底層繼承要代理類來實(shí)現(xiàn)(如果被代理類被final關(guān)鍵字所修飾,那么抱歉會(huì)失?。?。

4、使用注意:

  • 如果要被代理的對(duì)象是個(gè)實(shí)現(xiàn)類,那么Spring會(huì)使用JDK動(dòng)態(tài)代理來完成操作(Spirng默認(rèn)采用JDK動(dòng)態(tài)代理實(shí)現(xiàn)機(jī)制);
  • 如果要被代理的對(duì)象不是個(gè)實(shí)現(xiàn)類那么,Spring會(huì)強(qiáng)制使用CGLib來實(shí)現(xiàn)動(dòng)態(tài)代理。

四、JDK 和 CGLib動(dòng)態(tài)代理性能對(duì)比-教科書上的描述

我們不管是看書還是看文章亦或是我那個(gè)上搜索參考答案,可能很多時(shí)候,都可以找到如下的回答:

關(guān)于兩者之間的性能的話,JDK動(dòng)態(tài)代理所創(chuàng)建的代理對(duì)象,在以前的JDK版本中,性能并不是很高,雖然在高版本中JDK動(dòng)態(tài)代理對(duì)象的性能得到了很大的提升,但是他也并不是適用于所有的場景。主要體現(xiàn)在如下的兩個(gè)指標(biāo)中:

1、CGLib所創(chuàng)建的動(dòng)態(tài)代理對(duì)象在實(shí)際運(yùn)行時(shí)候的性能要比JDK動(dòng)態(tài)代理高不少,有研究表明,大概要高10倍;

2、但是CGLib在創(chuàng)建對(duì)象的時(shí)候所花費(fèi)的時(shí)間卻比JDK動(dòng)態(tài)代理要多很多,有研究表明,大概有8倍的差距;

3、因此,對(duì)于singleton的代理對(duì)象或者具有實(shí)例池的代理,因?yàn)闊o需頻繁的創(chuàng)建代理對(duì)象,所以比較適合采用CGLib動(dòng)態(tài)代理,反正,則比較適用JDK動(dòng)態(tài)代理。

結(jié)果是不是如上邊1、2、3條描述的那樣哪?下邊我們做一些小實(shí)驗(yàn)分析一下!

五、性能測試

1、首先有幾個(gè)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) {
    //創(chuàng)建測試對(duì)象
    Target nativeTest = new TargetImpl();
    Target dynamicProxy = JdkDynamicProxyTest.newProxyInstance(nativeTest);
    Target cglibProxy = CglibProxyTest.newProxyInstance(TargetImpl.class);
    //預(yù)熱一下
    int preRunCount = 10000;
    runWithoutMonitor(nativeTest, preRunCount);
    runWithoutMonitor(cglibProxy, preRunCount);
    runWithoutMonitor(dynamicProxy, preRunCount);
    //執(zhí)行測試
    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、測試結(jié)果

(1)JDK 1.6

(2)JDK 1.7

(3)JDK 1.8

經(jīng)過多次試驗(yàn),可以看出平均情況下的話,JDK動(dòng)態(tài)代理的運(yùn)行速度已經(jīng)逐漸提高了,在低版本的時(shí)候,運(yùn)行的性能可能不如CGLib,但是在1.8版本中運(yùn)行多次,基本都可以得到一致的測試結(jié)果,那就是JDK動(dòng)態(tài)代理已經(jīng)比CGLib動(dòng)態(tài)代理快了!

但是JDK動(dòng)態(tài)代理和CGLib動(dòng)態(tài)代理的適用場景還是不一樣的哈!

六、總結(jié)

最終的測試結(jié)果大致是這樣的,在1.6和1.7的時(shí)候,JDK動(dòng)態(tài)代理的速度要比CGLib動(dòng)態(tài)代理的速度要慢,但是并沒有教科書上的10倍差距,在JDK1.8的時(shí)候,JDK動(dòng)態(tài)代理的速度已經(jīng)比CGLib動(dòng)態(tài)代理的速度快很多了,希望小伙伴在遇到這個(gè)問題的時(shí)候能夠有的放矢!

Spring AOP中的JDK和CGLib動(dòng)態(tài)代理關(guān)于這個(gè)知識(shí)點(diǎn)很重要,關(guān)于兩者之間性能的對(duì)比經(jīng)過測試實(shí)驗(yàn)已經(jīng)有了一個(gè)初步的結(jié)果,以后再有人問你Spring AOP,不要簡單的說JDK動(dòng)態(tài)代理和CGLib這兩個(gè)了,是時(shí)候的可以拋出來對(duì)兩者之間區(qū)別的理解,是有加分的哦!

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。如果你想了解更多相關(guān)內(nèi)容請(qǐng)查看下面相關(guān)鏈接

相關(guān)文章

  • 基于java math API 的詳細(xì)解釋說明

    基于java math API 的詳細(xì)解釋說明

    本篇文章是對(duì)java math API進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • Mybatis中兼容多數(shù)據(jù)源的databaseId(databaseIdProvider)的簡單使用方法

    Mybatis中兼容多數(shù)據(jù)源的databaseId(databaseIdProvider)的簡單使用方法

    本文主要介紹了Mybatis中兼容多數(shù)據(jù)源的databaseId(databaseIdProvider)的簡單使用方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-07-07
  • JavaWeb中JavaMail創(chuàng)建郵件和發(fā)送郵件

    JavaWeb中JavaMail創(chuàng)建郵件和發(fā)送郵件

    這篇文章主要介紹了JavaWeb中JavaMail創(chuàng)建郵件和發(fā)送郵件,較為詳細(xì)的分析了JavaMail發(fā)送郵件的用法,是非常實(shí)用的技巧,需要的朋友可以參考下
    2015-12-12
  • ?java簡介及環(huán)境搭建

    ?java簡介及環(huán)境搭建

    這篇文章主要介紹了java簡介及環(huán)境搭建,文章主要介紹Java的發(fā)展史及環(huán)境搭建,對(duì)正在學(xué)Java的你有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-03-03
  • SpringBoot中支持Https協(xié)議的實(shí)現(xiàn)

    SpringBoot中支持Https協(xié)議的實(shí)現(xiàn)

    本文主要介紹了SpringBoot中支持Https協(xié)議的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • java如何更改數(shù)據(jù)庫中的數(shù)據(jù)

    java如何更改數(shù)據(jù)庫中的數(shù)據(jù)

    這篇文章主要介紹了java如何更改數(shù)據(jù)庫中的數(shù)據(jù),修改數(shù)據(jù)庫是數(shù)據(jù)庫操作必不可少的一部分,使用Statement接口中的excuteUpdate()方法可以修改數(shù)據(jù)表中的數(shù)據(jù),感興趣的朋友跟隨小編一起看看吧
    2021-11-11
  • Java讀寫Cookie記錄的方法

    Java讀寫Cookie記錄的方法

    這篇文章主要介紹了Java讀寫Cookie記錄的方法,實(shí)例分析了java針對(duì)cookie記錄讀取與寫入的技巧,需要的朋友可以參考下
    2015-05-05
  • 解決	Spring RestTemplate post傳遞參數(shù)時(shí)報(bào)錯(cuò)問題

    解決 Spring RestTemplate post傳遞參數(shù)時(shí)報(bào)錯(cuò)問題

    本文詳解說明了RestTemplate post傳遞參數(shù)時(shí)報(bào)錯(cuò)的問題及其原由,需要的朋友可以參考下
    2020-02-02
  • Hibernate持久化對(duì)象生命周期原理解析

    Hibernate持久化對(duì)象生命周期原理解析

    這篇文章主要介紹了Hibernate持久化對(duì)象生命周期原理解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • Mybatis中@Param的用法和作用詳解

    Mybatis中@Param的用法和作用詳解

    這篇文章主要介紹了Mybatis中@Param的用法和作用,在文中給大家補(bǔ)充了spring中@param和mybatis中@param使用區(qū)別,需要的朋友可以參考下
    2017-09-09

最新評(píng)論