1. 程式人生 > >Spring AOP中的JDK和CGLib動態代理哪個效率更高?

Spring AOP中的JDK和CGLib動態代理哪個效率更高?

一、背景

今天有小夥伴面試的時候被問到: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這兩個了,是時候的可以丟擲來對兩者之間區別的理解,是有加分的哦!

參考文章:

相關推薦

Spring AOPJDKCGLib動態代理哪個效率

一、背景 今天有小夥伴面試的時候被問到:Spring AOP中JDK 和 CGLib動態代理哪個效率更高? 二、基本概念 首先,我們知道Spring AOP的底層實現有兩種方式:一種是JDK動態代理,另一種是CGLib的方式。 自Java 1.3以後

SpringAOP兩種代理機制對比(JDKCGLib動態代理

Sprign 動態代理機制 Spirng的AOP的動態代理實現機制有兩種,分別是: 1)JDK動態代理: 具體實現原理: 1、通過實現InvocationHandlet介面建立自己的呼叫處理器 2、

代理模式】jdkcglib動態代理實現的區別

jdk和cglib動態代理實現的區別 1、jdk動態代理生成的代理類和委託類實現了相同的介面; 2、cglib動態代理中生成的位元組碼更加複雜,生成的代理類是委託類的子類,且不能處理被final關鍵字

JDKCGLIB動態代理區別

轉自:https://blog.csdn.net/yhl_jxy/article/details/80635012 一 JDK和CGLIB動態代理原理 1、JDK動態代理 利用攔截器(攔截器必須實現InvocationHanlder)加上反射機制生成一個實現代理介面的匿名類, 在呼叫具體

JDKCGLIB動態代理原理

一 JDK和CGLIB動態代理原理 1、JDK動態代理 利用攔截器(攔截器必須實現InvocationHanlder)加上反射機制生成一個實現代理介面的匿名類, 在呼叫具體方法前呼叫InvokeHandler來處理。 2、CGLiB動態代理 利用ASM開源包,對代理物件類的class檔案載入

Spring底層AOP的原理示例(JDK動態代理cglib動態代理

1 JDK動態代理(必須要有介面) 介面 package com.itykd.dao; public interface UserDao { void save(); void update(); void find(); void delete(); } 實現

Spring AOP之---基於JDK動態代理CGLib動態代理AOP實現

AOP(面向切面程式設計)是OOP的有益補充,它只適合那些具有橫切邏輯的應用場合,如效能監測,訪問控制,事物管理,日誌記錄等。至於怎麼理解橫切邏輯,敲完例項程式碼也就明白了。 為什麼要使用AOP,舉個栗子:需要監測一些方法的執行所消耗的時間,在每個方法開始

Spring AOP底層實現- JDK動態代理CGLIB動態代理

Spring AOP是執行時織入的,那麼執行時織入到底是怎麼實現的呢?答案就是代理物件。 代理又可以分為靜態代理和動態代理。 靜態代理:由程式設計師建立或特定工具自動生成原始碼,再對其編譯。在程式執行前,代理類的.class檔案就已經存在了。

spring aop原理 JDK動態代理CGLIB動態代理

lan ble -- 自定義 and ets spec gen ase Spring的兩大特性是IOC和AOPIOC負責將對象動態的註入到容器,從而達到一種需要誰就註入誰,什麽時候需要就什麽時候註入的效果。理解spring的ioc也很重要。但是今天主要來和大家講講aop。A

AOPJDK動態代理CGLib動態代理

測試結果 edit print handle es2017 brush 類庫 構建 sets 一、JDK動態代理 JDK內置的Proxy動態代理可以在運行時動態生成字節碼,而沒必要針對每個類編寫代理類。中間主要使用到了一個接口InvocationHandler與Proxy

Spring的兩種代理方式:JDK動態代理CGLIB動態代理

轉自 :https://blog.csdn.net/cckevincyh/article/details/54962920   代理模式 代理模式的英文叫做Proxy或Surrogate,中文都可譯為”代理“,所謂代理,就是一個人或者一個機構代表另一個人或者另一個機構採取行動。

Spring JDK動態代理CGLIB動態代理

的雙方各和進口量認同感和基你妹, 狂歡節空虛,嗎了 離開了是發;是否  進口量嗎, 的雙方各和進口量認同感和基你妹, 狂歡節空虛,嗎了 離開了是發;是否  進口量嗎, 的雙方各和進口量認同感和基你妹, 狂歡節空虛,嗎了 離開了是發;是否  進口量嗎, 

Spring 靜態代理+JDK動態代理CGLIB動態代理

代理分為兩種:靜態代理  動態代理 靜態代理:本質上會在硬碟上建立一個真正的物理類 動態代理:本質上是在記憶體中構建出一個類。 如果多個類需要進行方法增強,靜態代理則需要建立多個物理類,佔用磁碟空間。而動態代理則是在記憶體中建立,不會對磁碟進行影響。 靜態代理和JDK動態代理需要有介面。  CGLIB

JDK動態代理CGLIB動態代理,實現Spring註解管理事務區別。

註解式事務配置 1.JDK動態代理 <tx:annotation-driven transaction-manager="txManager"/>  預設啟用JDK動態代理,JDK只能代理介面不能代理類。 @Transactional註解可以標註在

由service層介面有什麼用?引申到基於JDK原生CGLIB動態代理實現spring事務管理的機制的思考

問題1:Services層為什麼要用(Services介面 類 + Services介面實現 類)分開,這樣做有什麼好處? 總結: 1.程式設計介面化, 2.Spring的事物管理預設用的是java動態代理。 問題2:Spring事物管理實現的機制

Spring學習總結(二)——靜態代理JDKCGLIB動態代理AOP+IoC

AOP(Aspect Oriented Programming)意為:面向切面程式設計,通過預編譯方式和執行期動態代理實現程式功能的統一維護的一種技術。AOP是OOP的延續,是軟體開發中的一個熱點,也是Spring框架中的一個重要內容,是函數語言程式設計的一種衍生範型。利用AOP可以對業務邏輯的各個部分進行隔

spring AOP 動態代理 jkd動態代理cglib動態代理 hibernate使用cglib延遲載入

spring 的AOP 實現 可以使用jdk的動態代理,也可以使用cglib的動態代理 先說下兩者區別: 靜態代理:代理之前就已經知道了代理者和被代理者 動態代理:代理之前並不清楚,在執行時使用反射機制動態生成代理類的位元組碼 無需我們手動編寫它的原始碼

AOP的底層實現-CGLIB動態代理JDK動態代理

        AOP是目前Spring框架中的核心之一,在應用中具有非常重要的作用,也是Spring其他元件的基礎。它是一種面向切面程式設計的思想。關於AOP的基礎知識,相信多數童鞋都已經瞭如指掌,我們就略過這部分,來講解下AOP的核心功能的底層實現機制:如何用動態代理來

SSM(六)JDK動態代理Cglib動態代理

sys .com images 織入 load obj spa -1 instance 1.Cglib動態代理 目標類: 1 package cn.happy.proxy.cglib; 2 3 public class Service { 4 publ

03、動態代理--JDK動態代理CGLib動態代理的組合實例

listen -- offer pri eth err imp instance music package com.offer.note.Java基礎.動態代理.CGLib引入增強; public interface Browser { void visitI