動(dòng)態(tài)代理是指代理類對(duì)象在程序運(yùn)行時(shí)由JVM根據(jù)反射機(jī)制動(dòng)態(tài)生成的。動(dòng)態(tài)代理不需要定義代理類的.java源文件。
動(dòng)態(tài)代理其實(shí)就是jdk運(yùn)行期間,動(dòng)態(tài)創(chuàng)建class字節(jié)碼并加載到JVM。
動(dòng)態(tài)代理的實(shí)現(xiàn)方式常用的有兩種:使用JDK代理代理,與通過CGLIB動(dòng)態(tài)代理。
jdk動(dòng)態(tài)代理是基于Java的反射機(jī)制實(shí)現(xiàn)的。使用jdk中接口和類實(shí)現(xiàn)代理對(duì)象的動(dòng)態(tài)創(chuàng)建。
Jdk的動(dòng)態(tài)要求目標(biāo)對(duì)象必須實(shí)現(xiàn)接口,這是java設(shè)計(jì)上的要求。
從jdk1.3以來,java語言通過java.lang.reflect包提供三個(gè)類支持代理模式Proxy, Method和InovcationHandler。
⒈ InvocationHandler接口
InvocationHandler接口叫做調(diào)用處理器,負(fù)責(zé)完調(diào)用目標(biāo)方法,并增強(qiáng)功能。
通過代理對(duì)象執(zhí)行目標(biāo)接口中的方法,會(huì)把方法的調(diào)用分派給調(diào)用處理器(InvocationHandler)的實(shí)現(xiàn)類,執(zhí)行實(shí)現(xiàn)類中的invoke()方法,我們需要把功能代理寫在invoke()方法中 。
接口中只有一個(gè)方法:
在invoke方法中可以截取對(duì)目標(biāo)方法的調(diào)用。在這里進(jìn)行功能增強(qiáng)。Java的動(dòng)態(tài)代理是建立在反射機(jī)制之上的。
實(shí)現(xiàn)了InvocationHandler接口的類用于加強(qiáng)目標(biāo)類的主業(yè)務(wù)邏輯。這個(gè)接口中有一個(gè)方法invoke(),具體加強(qiáng)的代碼邏輯就是定義在該方法中的。通過代理對(duì)象執(zhí)行接口中的方法時(shí),會(huì)自動(dòng)調(diào)用invoke()方法。
invoke()方法的介紹如下:
public Object invoke ( Object proxy, Method method, Object[] args)
proxy:代表生成的代理對(duì)象
method:代表目標(biāo)方法
args:代表目標(biāo)方法的參數(shù)
第一個(gè)參數(shù)proxy是jdk在運(yùn)行時(shí)賦值的,在方法中直接使用,第二個(gè)參數(shù)后面介紹,第三個(gè)參數(shù)是方法執(zhí)行的參數(shù), 這三個(gè)參數(shù)都是jdk運(yùn)行時(shí)賦值的,無需程序員給出。
⒉Method 類
invoke()方法的第二個(gè)參數(shù)為Method類對(duì)象,該類有一個(gè)方法也叫invoke(),可以調(diào)用目標(biāo)方法。這兩個(gè)invoke()方法,雖然同名,但無關(guān)。
public Object invoke ( Object?obj, Object...?args)
obj:表示目標(biāo)對(duì)象
args:表示目標(biāo)方法參數(shù),就是其上一層invoke方法的第三個(gè)參數(shù)
該方法的作用是:調(diào)用執(zhí)行obj對(duì)象所屬類的方法,這個(gè)方法由其調(diào)用者M(jìn)ethod對(duì)象確定。
在代碼中,一般的寫法為method.invoke(target, args);其中,method為上一層invoke方法的第二個(gè)參數(shù)。這樣,即可調(diào)用了目標(biāo)類的目標(biāo)方法。
⒊Proxy類
通過JDK的java.lang.reflect.Proxy類實(shí)現(xiàn)動(dòng)態(tài)代理,會(huì)使用其靜態(tài)方法newProxyInstance(),依據(jù)目標(biāo)對(duì)象、業(yè)務(wù)接口及調(diào)用處理器三者,自動(dòng)生成一個(gè)動(dòng)態(tài)代理對(duì)象。
public static newProxyInstance ( ClassLoader loader, Class<?>[] interfaces,
InvocationHandler handler)
loader:目標(biāo)類的類加載器,通過目標(biāo)對(duì)象的反射可獲取
interfaces:目標(biāo)類實(shí)現(xiàn)的接口數(shù)組,通過目標(biāo)對(duì)象的反射可獲取
handler:調(diào)用處理器。
jdk動(dòng)態(tài)代理實(shí)現(xiàn)
jdk動(dòng)態(tài)代理是代理模式的一種實(shí)現(xiàn)方式,其只能代理接口。
實(shí)現(xiàn)步驟:
① 新建一個(gè)接口,作為目標(biāo)接口
② 為接口創(chuàng)建一個(gè)實(shí)現(xiàn)類,是目標(biāo)類
③ 創(chuàng)建類實(shí)現(xiàn)java.lang.reflect.InvocationHandler接口,調(diào)用目標(biāo)方法并增加其他功能代碼
④ 創(chuàng)建動(dòng)態(tài)代理對(duì)象,使用Proxy.newProxyInstance()方法,并把返回值強(qiáng)制轉(zhuǎn)為接口類型。
idea創(chuàng)建java project
工程名稱:ch02-dynamicproxy
⒈定義目標(biāo)接口
⒉定義目標(biāo)接口實(shí)現(xiàn)類
⒊定義調(diào)用處理程序
調(diào)用處理程序是實(shí)現(xiàn)了InvocationHandler的類,在invoke方法中增加業(yè)務(wù)功能。還需要?jiǎng)?chuàng)建有參構(gòu)造,參數(shù)是目標(biāo)對(duì)象。為的是完成對(duì)目標(biāo)對(duì)象的方法調(diào)用。
⒋創(chuàng)建動(dòng)態(tài)代理對(duì)象
執(zhí)行流程:
類圖:
CGLIB(Code Generation Library)是一個(gè)開源項(xiàng)目。是一個(gè)強(qiáng)大的,高性能,高質(zhì)量的Code生成類庫,它可以在運(yùn)行期擴(kuò)展Java類與實(shí)現(xiàn)Java接口。它廣泛的被許多AOP的框架使用,例如Spring AOP。
使用JDK的Proxy實(shí)現(xiàn)代理,要求目標(biāo)類與代理類實(shí)現(xiàn)相同的接口。若目標(biāo)類不存在接口,則無法使用該方式實(shí)現(xiàn)。
但對(duì)于無接口的類,要為其創(chuàng)建動(dòng)態(tài)代理,就要使用CGLIB來實(shí)現(xiàn)。CGLIB代理的生成原理是生成目標(biāo)類的子類,而子類是增強(qiáng)過的,這個(gè)子類對(duì)象就是代理對(duì)象。所以,使用CGLIB生成動(dòng)態(tài)代理,要求目標(biāo)類必須能夠被繼承,即不能是final的類。
cglib經(jīng)常被應(yīng)用在框架中,例如Spring ,Hibernate等。Cglib的代理效率高于Jdk。對(duì)于cglib一般的開發(fā)中并不使用。做了一個(gè)了解就可以。