JDK動(dòng)態(tài)代理詳細(xì)解析
一、說(shuō)明
在Java的動(dòng)態(tài)代理機(jī)制中,有兩個(gè)重要的類(lèi)和接口,一個(gè)是InvoInvocationHandler(接口)、Proxy(類(lèi)),這一個(gè)類(lèi)和接口是我們動(dòng)態(tài)代理所必須用到的。

優(yōu)點(diǎn):
- 對(duì)于實(shí)現(xiàn)了接口的類(lèi),可以直接使用基于接口的動(dòng)態(tài)代理進(jìn)行代理,非常方便
- 代理類(lèi)和被代理類(lèi)都必須實(shí)現(xiàn)同一個(gè)接口,能夠?qū)崿F(xiàn)對(duì)被代理對(duì)象的方法調(diào)用進(jìn)行統(tǒng)一管理。
- 性能上:在老版的jdk,jdk代理生成的類(lèi)速度快,通過(guò)反射調(diào)用慢,cglib是jdk代理速度的10倍左右,jdk在版本每次升級(jí)都會(huì)有很大的性能提升,cglib停滯不前,jdk7 8的動(dòng)態(tài)代理性能在1萬(wàn)次實(shí)驗(yàn)中比cglib要快20%左右
- jdk動(dòng)態(tài)代理如果目標(biāo)類(lèi)未實(shí)現(xiàn)接口則無(wú)法代理,cglib是通過(guò)繼承的方式來(lái)動(dòng)態(tài)代理,若目標(biāo)類(lèi)被final關(guān)鍵字修飾,則無(wú)法使用cglib做動(dòng)態(tài)代理
缺點(diǎn):
- 只能代理實(shí)現(xiàn)了接口的類(lèi),對(duì)于沒(méi)有實(shí)現(xiàn)接口的類(lèi)無(wú)法使用此種方式進(jìn)行代理。
- jdk動(dòng)態(tài)代理只提供實(shí)現(xiàn)接口的目標(biāo)類(lèi)代理,不支持沒(méi)有實(shí)現(xiàn)接口的目標(biāo)類(lèi)的代理。如果目標(biāo)類(lèi)沒(méi)有實(shí)現(xiàn)接口,只能用cglib代理
二、主要類(lèi)方法的說(shuō)明
InvocationHandler接口
每個(gè)動(dòng)態(tài)代理類(lèi)都必須實(shí)現(xiàn)InvocationHandler接口,并且每個(gè)代理類(lèi)的實(shí)例都關(guān)聯(lián)到了一個(gè)handler,當(dāng)我們通過(guò)代理對(duì)象調(diào)用一個(gè)方法的時(shí)候,這個(gè)方法的調(diào)用就會(huì)被轉(zhuǎn)發(fā)為由InvocationHandler接口的invoke方法來(lái)進(jìn)行調(diào)用。
InvocationHandler接口的invoke方法
- Object invoke(Object proxy, Method method, Object[] args) throws Throwable
- proxy: - 指代我們所代理的那個(gè)真實(shí)對(duì)象
- method: - 指代的是我們所要調(diào)用真實(shí)對(duì)象的某個(gè)方法的Method對(duì)象
- args: - 指代的是調(diào)用真實(shí)對(duì)象某個(gè)方法時(shí)接受的參數(shù)
**proxy存在的意義:**
1. 可以使用反射獲取代理對(duì)象的信息(也就是proxy.getClass().getName())。
2. 可以將代理對(duì)象返回以進(jìn)行連續(xù)調(diào)用,這就是proxy存在的目的,因?yàn)閠his并不是代理對(duì)象。
Proxy 類(lèi)
Proxy類(lèi)的作用就是用來(lái)動(dòng)態(tài)創(chuàng)建一個(gè)代理類(lèi)對(duì)象的類(lèi),它提供了許多的方法,但是我們用的最多的就是newProxyInstance這個(gè)方法
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
- loader: 一個(gè)ClassLoader對(duì)象,定義了由哪個(gè)ClassLoader對(duì)象來(lái)對(duì)生成的代理對(duì)象進(jìn)行加載
- interfaces: 一個(gè)Interface對(duì)象的數(shù)組,表示的是我將要給我需要代理的對(duì)象提供一組什么接口,如果我提供了一組接口給它,那么這個(gè)代理對(duì)象就宣稱(chēng)實(shí)現(xiàn)了該接口(多態(tài)),這樣我就能調(diào)用這組接口中的方法了
- h: 一個(gè)InvocationHandler對(duì)象,表示的是當(dāng)我這個(gè)動(dòng)態(tài)代理對(duì)象在調(diào)用方法的時(shí)候,會(huì)關(guān)聯(lián)到哪一個(gè)InvocationHandler對(duì)象上
打印代理的類(lèi):
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
三、關(guān)鍵步驟
創(chuàng)建InvocationHandler實(shí)現(xiàn)類(lèi)
public class MapperProxy<T> implements InvocationHandler {
private Class<T> proxyInterface;
//這里可以維護(hù)一個(gè)緩存,存這個(gè)接口的方法抽象的對(duì)象
MapperProxy(Class<T> proxyInterface){
this.proxyInterface = proxyInterface;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("執(zhí)行前...");
Constructor constructor = proxyInterface.getConstructor();
Object o = constructor.newInstance();
method.invoke(o,args);
System.out.println("執(zhí)行后");
return null;
}
}
通過(guò) Proxy.newProxyInstance() 創(chuàng)建代理實(shí)例
Fly fly = (Fly) Proxy.newProxyInstance(MyFly.class.getClassLoader(),new Class[]{Fly.class, Fly.Fly2.class},new MapperProxy<>(MyFly.class));

- 創(chuàng)建代理類(lèi)- Class<?> cl = getProxyClass0(loader, intfs);
- 獲取有參構(gòu)造器 Constructor<?> cons = cl.getConstructor(constructorParams);這里的參數(shù)就是InvocationHandler
- 通過(guò)構(gòu)造器創(chuàng)建代理實(shí)例-參數(shù)就是方法的第三個(gè)參數(shù)
分析生成的代理類(lèi)


結(jié)論
- 代理類(lèi)會(huì)繼承Proxy ,這里也就解釋了為什么通過(guò)JDK生成的代理無(wú)法代理非接口實(shí)現(xiàn)類(lèi)了
- 代理類(lèi)實(shí)現(xiàn)了傳入的所有接口類(lèi)型
調(diào)用代理類(lèi)的doFly()
這里會(huì)調(diào)用 super.h.invoke(this, m3, (Object[])null);
super.h就是我們?cè)賱?chuàng)建代理對(duì)象是傳入的MapperProxy,所有這里會(huì)執(zhí)行MapperProxy.invoke方法(在這里我們就可動(dòng)態(tài)的對(duì)該執(zhí)行方法進(jìn)行增強(qiáng))

到此這篇關(guān)于JDK動(dòng)態(tài)代理詳細(xì)解析的文章就介紹到這了,更多相關(guān)JDK動(dòng)態(tài)代理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- JDK動(dòng)態(tài)代理提高代碼可維護(hù)性和復(fù)用性利器
- Spring之AOP兩種代理機(jī)制對(duì)比分析(JDK和CGLib動(dòng)態(tài)代理)
- 基于jdk動(dòng)態(tài)代理和cglib動(dòng)態(tài)代理實(shí)現(xiàn)及區(qū)別說(shuō)明
- 基于JDK動(dòng)態(tài)代理原理解析
- Java JDK與cglib動(dòng)態(tài)代理有什么區(qū)別
- 解讀jdk動(dòng)態(tài)代理為什么必須實(shí)現(xiàn)接口
- JDK動(dòng)態(tài)代理原理:只能代理接口,不能代理類(lèi)問(wèn)題
- Java中JDK動(dòng)態(tài)代理的超詳細(xì)講解
相關(guān)文章
SpringBoot yml配置文件調(diào)用過(guò)程解析
這篇文章主要介紹了SpringBoot yml配置文件調(diào)用過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11
SpringBoot實(shí)現(xiàn)單文件與多文件上傳
本次例子不基于第三方存儲(chǔ)(如七牛云對(duì)象存儲(chǔ)、阿里云對(duì)象存儲(chǔ)、騰訊云對(duì)象存儲(chǔ)等),僅基于本地存儲(chǔ)。本文主要內(nèi)容如下:公共文件存儲(chǔ)代碼;單文件上傳代碼;多文件上傳代碼2021-05-05
spring boot 防止重復(fù)提交實(shí)現(xiàn)方法詳解
這篇文章主要介紹了spring boot 防止重復(fù)提交實(shí)現(xiàn)方法,結(jié)合實(shí)例形式詳細(xì)分析了spring boot 防止重復(fù)提交具體配置、實(shí)現(xiàn)方法及操作注意事項(xiàng),需要的朋友可以參考下2019-11-11
SpringBoot解決同名類(lèi)導(dǎo)致的bean名沖突bean name conflicts問(wèn)題
這篇文章主要介紹了SpringBoot解決同名類(lèi)導(dǎo)致的bean名沖突bean name conflicts問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06
教你用Springboot實(shí)現(xiàn)攔截器獲取header內(nèi)容
項(xiàng)目中遇到一個(gè)需求,對(duì)接上游系統(tǒng)是涉及到需要增加請(qǐng)求頭,請(qǐng)求頭的信息是動(dòng)態(tài)獲取的,需要?jiǎng)討B(tài)從下游拿到之后轉(zhuǎn)給上游,文中非常詳細(xì)的介紹了該需求的實(shí)現(xiàn),需要的朋友可以參考下2021-05-05

