Java中的動態代理

動態代理

一、先談靜態代理

對於靜態代理,咱們已經很熟悉了。咱們擁有一個抽象類,真實類繼承自抽象類並重寫其業務方法,代理類持有真實類的對象實例,在重寫業務方法中經過調用真實類的方法,而且添加本身的邏輯。這樣代理類就實現了對真實類的行爲代理。java

sp-1

靜態代理的缺點在於,咱們須要實現多個代理類,這無疑是很崩潰的。spring

二、JDK動態代理

優勢先行:咱們說靜態代理的缺點在於須要爲每個真實類都生成一個對應的代理類,這樣就很繁瑣。動態代理呢,咱們是動態生成目標類的動態代理類,根本不須要爲每個只須要定義一個動態代理類,就能夠代理全部、無數的真實類;固然咱們須要一個類來實現 InvocationHandler接口便可。也就是說咱們只須要一個類便可。

java中提供了一個接口InvocationHandlerProxy類來實現動態代理。數組

InvocationHandler接口是代理實例用來調用處理程序(代理行爲)的接口,它只有一個方法。接收的參數爲:動態代理對象,代理對象調用的接口方法的實例,執行參數。框架

dp-1

就以下面的Market類實現了InvocationHandler接口,內部的Object target用來持有動態代理的真實對象實例,bind方法用來接收外部真實類對象傳遞給target。而後重點就是重寫invoke方法了,這個方法和下面的Proxy類一塊兒講解。性能

這個類是用來處理動態代理的邏輯的,它的做用是接收生成的動態代理對象和被代理對象實現的接口與運行時的參數,這樣就能攔截對接口方法的調用,從而實現本身的攔截邏輯。

dp-2

以下圖,咱們在測試類中,使用Proxy類的newProxyInstance靜態方法來生成動態代理對象。該方法很重要,傳入的參數:被代理類的類加載器,被代理類的所實現的接口(用數組保存的),實現了InvocationHandler的接口的動態代理處理類。測試

這個類用來生成了動態代理的對象,很神奇的是,咱們調用接口的方法時,就發現方法被攔截了,這時候生成的是動態代理對象,因此接口方法的邏輯已是被攔截的邏輯了。(這裏咱們當作是一個新的動態代理的對象就行了)

dp-3

爲避免混亂,將包、類的結構給出:spa

dp-4

動態代理類的命名規則:包名+$Proxy+id,同一個接口的實現類的id是相同的。代理

dp-5

JDK動態代理的總結(不足)

1.JDK動態代理是經過接口中的方法名,在動態生成的代理類中調用業務實現類的同名方法。所以,對一個類而言,若是想要對它使用JDK動態代理,那麼這個類就必須實現接口interface,而且咱們攔截的是其接口聲明的方法才行。簡言之,JDK動態代理只能對實現了接口的類生成代理,而不能針對未實現接口的類code

2.咱們發現,在JDK動態代理的實現過程當中,咱們沒法對接口中的各個方法都實現一段獨有的邏輯(若是被代理類實現的接口有多個方法,咱們的動態代理的攔截邏輯卻只有一段,全部的方法被攔截時都是用相同的邏輯來處理)。這是一個很大的問題。對象

3.JDK動態代理生成的動態代理類,是沒法調用原業務類本身擁有的方法的(即接口中沒有聲明的方法)。要明白,動態代理類的存在乎義是爲了攔截方法並修改邏輯;而JDK動態代理的侷限性之一就是隻能攔截接口所聲明的方法。

4.JDK動態代理是利用反射機制生成一個實現代理接口的匿名類,在調用具體方法前調用InvokeHandler來處理。

三、CGLib動態代理

和JDK動態代理不一樣的是,CGLib動態代理解決了JDK動態代理的第一個不足。也就是說,若是一個類沒有實現接口,那麼咱們還可使用CGLib來生成其動態代理對象。

這裏咱們要講到MethodInterceptor接口了。注意,CGLib動態代理並非JDK中的類,它是外部的lib包。

dp-6

咱們還須要結合動態代理對象的生成來說解。

dp-7

一樣,咱們只須要定義一個動態代理的處理類便可。實現了MethodInterceptor接口的類,須要重寫intercept方法,用來攔截被代理類的方法調用。而後在該類中定義一個生產動態代理對象的方法,該方法接收被代理類的對象,而後攔截方法,設置調用攔截方法的邏輯,最後返回建立的動態代理對象。

CGLib動態代理的總結

1.CGlib動態代理的原理是經過繼承業務類,生成的動態代理類是業務類的子類,經過重寫業務方法進行代理。 由於動態代理類是繼承自業務類,因此該類和方法不能聲明成final(沒法繼承或重寫)。

2.同JDK動態代理同樣,CGLib動態代理也沒法作到對業務類的每一個方法都實現不一樣的攔截邏輯。

JDK動態代理和CGLib動態代理的比較

1.JDK動態代理是面向接口,在建立代理實現類時比CGLib要快,建立動態代理類的速度快。

2.CGLib動態代理是經過字節碼底層繼承要代理類來實現(若是被代理類被final關鍵字所修飾,那麼會失敗),在建立代理這一塊沒有JDK動態代理快,可是運行速度比JDK動態代理要快。

3.2者最終都是生成了一個新的動態代理類對象。

Spring中的動態代理

一、Spring在選擇用JDK仍是CGLiB的依據?

(1)當Bean實現接口時,Spring就會用JDK的動態代理。

(2)當Bean沒有實現接口時,Spring使用CGlib實現。

(3)能夠強制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>

二、CGlib比JDK快?

(1)在對JDK動態代理與CGlib動態代理的代碼實驗中看,1W次執行下,JDK7及8的動態代理性能比CGlib要好20%左右。

(2)使用CGLib實現動態代理,CGLib底層採用ASM字節碼生成框架,使用字節碼技術生成代理類,比使用Java反射效率要高。惟一須要注意的是,CGLib不能對聲明爲final的方法進行代理,由於CGLib原理是動態生成被代理類的子類。

相關文章
相關標籤/搜索