一、背景
昨天一位小伙伴面試的時候被問到: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動態代理具體實現原理:
-
通過實現InvocationHandler接口創建自己的調用處理器;
-
通過為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.6和1.7的時候,JDK動態代理的速度要比CGLib動態代理的速度要慢,但是並沒有教科書上的10倍差距,在JDK1.8的時候,JDK動態代理的速度已經比CGLib動態代理的速度快很多了,希望小伙伴在遇到這個問題的時候能夠有的放矢!
Spring AOP中的JDK和CGLib動態代理關於這個知識點很重要,關於兩者之間性能的對比經過測試實驗已經有了一個初步的結果,以后再有人問你Spring AOP,不要簡單的說JDK動態代理和CGLib這兩個了,是時候的可以拋出來對兩者之間區別的理解,是有加分的哦!
轉載: https://blog.51cto.com/14227759/2400378