一,打破砂鍋問到底
- 什么事代理模式独榴?
- 什么是靜態(tài)代理僧叉,有啥缺陷?
- 什么是動態(tài)代理棺榔?
- JDK動態(tài)代理是如何動態(tài)生成類的瓶堕?
- 什么是cglib動態(tài)代理?有啥區(qū)別
- 動態(tài)代理是如何運(yùn)用在開發(fā)中的
二症歇,曉之以理郎笆,動之以碼
測試代碼:Github
1. 代理模式谭梗?
定義:
代理模式是給真實(shí)對象提供一個(gè)代理對象,并由代理對象控制對真實(shí)對象的引用宛蚓。
作用:
1.通過引入代理對象的方式來間接訪問真實(shí)對象激捏,防止直接訪問真實(shí)對象給系統(tǒng)帶來的不必要復(fù)雜性;
2.通過代理對象對原有的業(yè)務(wù)增強(qiáng)凄吏;
UML類圖:
代理模式一般會有三個(gè)角色:
抽象對象: 指代理對象和真實(shí)對象對外提供的公共方法远舅,一般為一個(gè)接口。
真實(shí)對象: 需要實(shí)現(xiàn)抽象對象接口痕钢,定義了真實(shí)對象所要實(shí)現(xiàn)的業(yè)務(wù)邏輯图柏,以便供代理對象調(diào)用。也就是真正的業(yè)務(wù)邏輯在此任连。
代理對象: 需要實(shí)現(xiàn)抽象對象接口蚤吹,并包含真實(shí)對象的引用從而操作真實(shí)對象,是訪問者與真實(shí)對象之間的代理随抠,并可以附加自己的操作裁着。將統(tǒng)一的流程控制都放到代理對象中處理。
2. 靜態(tài)代理
靜態(tài)代理在使用時(shí),需要定義接口或者父類,真實(shí)對象與代理對象一起實(shí)現(xiàn)相同的抽象對象接口或者是繼承相同父類拱她。
一般來說二驰,被代理對象和代理對象是一對一的關(guān)系,當(dāng)然一個(gè)代理對象對應(yīng)多個(gè)被代理對象也是可以的秉沼。
代碼實(shí)現(xiàn):
/**
* 抽象角色:指代理角色和真實(shí)角色對外提供的公共方法诸蚕,一般為一個(gè)接口
*/
public interface Subject {
String request(String data);
int response(int value);
}
/**
* 真實(shí)角色:需要實(shí)現(xiàn)抽象角色接口,定義了真實(shí)角色所要實(shí)現(xiàn)的業(yè)務(wù)邏輯氧猬,以便供代理角色調(diào)用。也就是真正的業(yè)務(wù)邏輯在此坏瘩。
*/
public static class RealSubject implements Subject, Subject2 {
@Override
public String request(String data) {
System.out.println("RealSubject-data=" + data);
return data;
}
@Override
public int response(int value) {
return value;
}
}
/**
* 代理角色:需要實(shí)現(xiàn)抽象角色接口盅抚,是真實(shí)角色的代理,通過真實(shí)角色的業(yè)務(wù)邏輯方法來實(shí)現(xiàn)抽象方法倔矾,并可以附加自己的操作妄均。將統(tǒng)一的流程控制都放到代理角色中處理!
*/
public static class ProxySubject implements Subject {
private Subject realSubject;
public ProxySubject(Subject realSubject) {
this.realSubject = realSubject;
}
@Override
public String request(String data) {
doSthBefore();
realSubject.request(data);
doSthAfter();
return data;
}
@Override
public int response(int value) {
return value;
}
/**
* 前置處理器
*/
private void doSthAfter() {
System.out.println("調(diào)用真實(shí)對象之后");
}
/**
* 后置處理器
*/
private void doSthBefore() {
System.out.println("調(diào)用真實(shí)對象之前");
}
}
測試代碼:
/**
* Demo1:靜態(tài)代理的實(shí)現(xiàn)
* 靜態(tài)代理在使用時(shí),需要定義接口或者父類,被代理對象與代理對象一起實(shí)現(xiàn)相同的接口或者是繼承相同父類哪自。
* 一般來說丰包,被代理對象和代理對象是一對一的關(guān)系,當(dāng)然一個(gè)代理對象對應(yīng)多個(gè)被代理對象也是可以的壤巷。
* 靜態(tài)代理邑彪,一對一則會出現(xiàn)時(shí)靜態(tài)代理對象量多、代碼量大胧华,從而導(dǎo)致代碼復(fù)雜寄症,可維護(hù)性差的問題宙彪,
* 一對多則代理對象會出現(xiàn)擴(kuò)展能力差的問題。
*/
private static void Demo1() {
System.out.println("-----Demo1-----\n\n");
System.out.println("-----靜態(tài)代理的實(shí)現(xiàn)-----");
Subject realSubject = new RealSubject();
Subject proxySubject = new ProxySubject(realSubject);
proxySubject.request("靜態(tài)代理類調(diào)用了");
}
運(yùn)行結(jié)果:
-----Demo1-----
-----靜態(tài)代理的實(shí)現(xiàn)-----
調(diào)用真實(shí)對象之前
RealSubject-data=靜態(tài)代理類調(diào)用了
調(diào)用真實(shí)對象之后
Process finished with exit code 0
靜態(tài)代理的缺點(diǎn):
違反開閉原則 導(dǎo)致擴(kuò)展和可維護(hù)性差:
- 一對一則會出現(xiàn)時(shí)靜態(tài)代理對象量多有巧、代碼量大释漆,從而導(dǎo)致代碼復(fù)雜,可維護(hù)性差的問題篮迎;
- 一對多容易導(dǎo)致代理對象會出現(xiàn)擴(kuò)展能力差的問題男图。
3. 動態(tài)代理
是指在使用時(shí)再創(chuàng)建代理類和實(shí)例
在java的動態(tài)代理機(jī)制中,有兩個(gè)重要的類或接口甜橱,一個(gè)是InvocationHandler接口逊笆、另一個(gè)則是 Proxy類,這個(gè)類和接口是實(shí)現(xiàn)我們動態(tài)代理所必須用到的渗鬼。
Proxy
Proxy是用來創(chuàng)建動態(tài)代理對象的實(shí)例對象的览露,只要得到了這個(gè)對象我們就能調(diào)用到真實(shí)對象的方法。
InvocationHandler
InvocationHandler接口是給動態(tài)代理對象實(shí)現(xiàn)的譬胎,負(fù)責(zé)處理真實(shí)對象的操作的差牛,
代碼實(shí)現(xiàn):
/**
* 動態(tài)代理生成工廠類
*/
public static class DynamicProxySubjectFactory implements InvocationHandler {
/**
* 持有的真實(shí)對象
*/
private Subject realSubject;
public DynamicProxySubjectFactory(Subject realSubject) {
this.realSubject = realSubject;
}
/**
* 通過Proxy獲得動態(tài)代理對象
*/
public Object getProxyInstance() {
System.out.println("---------------------getProxyInstance-----------------------");
/**
* Returns an instance of a proxy class for the specified interfaces * that dispatches method
* invocations to the specified invocation handler. 返回一個(gè)實(shí)現(xiàn)了抽象接口的代理對象,該實(shí)例將方法調(diào)用委托給invocation
* handler來調(diào)用堰乔。
*
* @param loader the class loader to define the proxy class loader 類加載器用于定義代理類
* @param interfaces the list of interfaces for the proxy class to implement interfaces
* 代理類要實(shí)現(xiàn)的接口列表
* @param h the invocation handler to dispatch method invocations to
* InvocationHandler委托調(diào)用代理對象的方法
* @return a proxy instance with the specified invocation handler of a * proxy class that is
* defined by the specified class loader * and that implements the specified interfaces
* 返回一個(gè)具有包含InvocationHandler的并由指定類加載器定義且實(shí)現(xiàn)了指定接口的代理對象
*/
System.out.println("---getProxyInstance-realSubject.getClass()=");
System.out.println(realSubject.getClass());//class com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject
System.out.println("---getProxyInstance-realSubject.getClass().getClassLoader()=");
System.out.println(realSubject.getClass().getClassLoader());//sun.misc.Launcher$AppClassLoader@18b4aac2
System.out.println("---getProxyInstance-realSubject.getClass().getInterfaces()[0]=");
for (Class<?> anInterface : realSubject.getClass().getInterfaces()) {
System.out.println(anInterface);//interface com.jay.java.DynamicProxy.test.ProxyMainTest$Subject
}
//獲取代理對象
Object proxyInstance = Proxy.newProxyInstance(
realSubject.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),
this);
System.out.println("---getProxyInstance-代理對象proxyInstance=");
System.out.println(proxyInstance.getClass().getName());//com.sun.proxy.$Proxy0
return proxyInstance;
}
/**
* Processes a method invocation on a proxy instance and returns * the result. This method will
* be invoked on an invocation handler * when a method is invoked on a proxy instance that it is
* * associated with.
*
* 對代理對象執(zhí)行方法調(diào)用并返回結(jié)果偏化。 該方法會被和代理對象關(guān)聯(lián)對InvocationHandler接口對象調(diào)用。 如:在代理對象中request方法
* super.h.invoke(this, m3, new Object[]{var1});
*
* @param proxy 調(diào)用該方法對代理對象
* @param method the {@code Method} instance corresponding to * the interface method invoked on
* the proxy instance. The declaring * class of the {@code Method} object will be the
* interface that * the method was declared in, which may be a superinterface of the * proxy
* interface that the proxy class inherits the method through.
* 與在代理對象上調(diào)用的接口方法相對應(yīng)的Method實(shí)例镐侯,Method實(shí)例是在聲明該方法的接口中被聲明對侦讨,這個(gè)接口也可能是代理類實(shí)現(xiàn)對代理接口的超接口
* @param args an array of objects containing the values of the * arguments passed in the method
* invocation on the proxy instance, * or {@code null} if interface method takes no
* arguments. * Arguments of primitive types are wrapped in instances of the * appropriate
* primitive wrapper class, such as * {@code java.lang.Integer} or {@code
* java.lang.Boolean}. 代理對象調(diào)用真實(shí)對象的方法所需要傳遞的參數(shù)值的對象數(shù)組;如果接口方法不接受參數(shù)苟翻,則為null韵卤。
* 基本類型的參數(shù)將被自動裝箱為包裝類型,例如Integer崇猫,Boolean沈条。
* @return the value to return from the method invocation on the * proxy instance. If the
* declared return type of the interface * method is a primitive type, then the value
* returned by * this method must be an instance of the corresponding primitive * wrapper
* class; otherwise, it must be a type assignable to the * declared return type. If the
* value returned by this method is * {@code null} and the interface method's return type is
* * primitive, then a {@code NullPointerException} will be * thrown by the method
* invocation on the proxy instance. If the * value returned by this method is otherwise not
* compatible with * the interface method's declared return type as described above, * a
* {@code ClassCastException} will be thrown by the method * invocation on the proxy
* instance.
* 代理實(shí)例上的方法調(diào)用返回的值。 如果接口方法的聲明的返回類型是原始類型诅炉,則此方法返回的值必須是對應(yīng)的原始包裝器類的實(shí)例蜡歹;
* 否則,它必須是可分配給聲明的返回類型的類型涕烧。
* 如果此方法返回的值為 null月而,并且接口方法的返回類型為原始類型,則在代理實(shí)例上的method調(diào)用將拋出NullPointerException议纯。
* 如果此方法返回的*值與上述接口方法的聲明的返回類型不兼容父款,則proxyinstance上的調(diào)用方法引發(fā)ClassCastException。
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// super.h.invoke(this, m3, new Object[]{var1});
System.out.println("---------------------invoke-----------------------");
System.out.println("---invoke-代理對象實(shí)例proxy=" + proxy.getClass().getSimpleName());//invoke-proxy=$Proxy0
System.out.println("---invoke-執(zhí)行的方法method=" + method.getName());//invoke-method=request
for (Object arg : args) {
System.out.println("---invoke-方法參數(shù)值args[i]=");
System.out.println(arg);//動態(tài)代理類調(diào)用了
System.out.println("---invoke-方法參數(shù)類型args[i].getClass().getSimpleName()=");
System.out.println(arg.getClass().getSimpleName());//String
}
//通過動態(tài)代理對象方法進(jìn)行增強(qiáng)
doSthAfter();
//反射執(zhí)行真實(shí)對象對應(yīng)的method
Object result = method.invoke(realSubject, args);
doSthBefore();
System.out.println("---invoke-方法返回值result=" + result);//result=動態(tài)代理類調(diào)用了
return result;
}
/**
* 前置處理
*/
private void doSthAfter() {
System.out.println("調(diào)用真實(shí)對象之后");
}
/**
* 后置處理
*/
private void doSthBefore() {
System.out.println("調(diào)用真實(shí)對象之前");
}
}
測試代碼:
/**
* Demo2:JDK動態(tài)代理的實(shí)現(xiàn)
* <p>
* 動態(tài)代理是指在使用時(shí)再創(chuàng)建代理類和實(shí)例
* 優(yōu)點(diǎn)
* 只需要1個(gè)動態(tài)代理類就可以解決創(chuàng)建多個(gè)靜態(tài)代理的問題,避免重復(fù)铛漓、多余代碼更強(qiáng)的靈活性
* 缺點(diǎn)
* 效率低溯香,相比靜態(tài)代理中 直接調(diào)用目標(biāo)對象方法,動態(tài)代理則需要先通過Java反射機(jī)制 從而 間接調(diào)用目標(biāo)對象方法
* 應(yīng)用場景局限浓恶,因?yàn)?Java 的單繼承特性(每個(gè)代理類都繼承了 Proxy 類)玫坛,即只能針對接口 創(chuàng)建 代理類,不能針對類創(chuàng)建代理類包晰。
* 在java的動態(tài)代理機(jī)制中湿镀,有兩個(gè)重要的類或接口,一個(gè)是InvocationHandler接口伐憾、另一個(gè)則是 Proxy類勉痴,這個(gè)類和接口是實(shí)現(xiàn)我們動態(tài)代理所必須用到的。
* InvocationHandler接口是給動態(tài)代理類實(shí)現(xiàn)的树肃,負(fù)責(zé)處理被代理對象的操作的蒸矛,
* Proxy是用來創(chuàng)建動態(tài)代理類實(shí)例對象的,因?yàn)橹挥械玫搅诉@個(gè)對象我們才能調(diào)用那些需要代理的方法胸嘴。
*/
private static void Demo2() {
System.out.println("-----Demo2-----\n\n");
System.out.println("-----JDK動態(tài)代理的實(shí)現(xiàn)-----");
//創(chuàng)建真實(shí)對象實(shí)例
Subject realSubject = new RealSubject();
//將真實(shí)對象傳入代理生產(chǎn)工廠類中
DynamicProxySubjectFactory proxySubjectFactory = new DynamicProxySubjectFactory(realSubject);
//獲取jdk動態(tài)生成的代理類
Subject proxySubject = (Subject) proxySubjectFactory.getProxyInstance();
//執(zhí)行真實(shí)對象的方法
proxySubject.request("動態(tài)代理類調(diào)用了");
proxySubject.response(521);
}
運(yùn)行結(jié)果:
-----Demo2-----
-----JDK動態(tài)代理的實(shí)現(xiàn)-----
---------------------getProxyInstance-----------------------
---getProxyInstance-realSubject.getClass()=
class com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject
---getProxyInstance-realSubject.getClass().getClassLoader()=
sun.misc.Launcher$AppClassLoader@18b4aac2
---getProxyInstance-realSubject.getClass().getInterfaces()[0]=
interface com.jay.java.DynamicProxy.test.ProxyMainTest$Subject
interface com.jay.java.DynamicProxy.test.ProxyMainTest$Subject2
---getProxyInstance-代理對象proxyInstance=
com.sun.proxy.$Proxy0
---------------------invoke-----------------------
---invoke-代理對象實(shí)例proxy=$Proxy0
---invoke-執(zhí)行的方法method=request
---invoke-方法參數(shù)值args[i]=
動態(tài)代理類調(diào)用了
---invoke-方法參數(shù)類型args[i].getClass().getSimpleName()=
String
調(diào)用真實(shí)對象之后
RealSubject-data=動態(tài)代理類調(diào)用了
調(diào)用真實(shí)對象之前
---invoke-方法返回值result=動態(tài)代理類調(diào)用了
---------------------invoke-----------------------
---invoke-代理對象實(shí)例proxy=$Proxy0
---invoke-執(zhí)行的方法method=response
---invoke-方法參數(shù)值args[i]=
521
---invoke-方法參數(shù)類型args[i].getClass().getSimpleName()=
Integer
調(diào)用真實(shí)對象之后
調(diào)用真實(shí)對象之前
---invoke-方法返回值result=521
Process finished with exit code 0
優(yōu)點(diǎn):
只需要1個(gè)動態(tài)代理類就可以解決創(chuàng)建多個(gè)靜態(tài)代理的問題雏掠,避免重復(fù)、多余代碼劣像,有更強(qiáng)的靈活性乡话。
缺點(diǎn):
效率低:相比靜態(tài)代理中 直接調(diào)用目標(biāo)對象方法,動態(tài)代理則需要先通過Java反射機(jī)制從而間接調(diào)用目標(biāo)對象方法
應(yīng)用場景局限:因?yàn)镴ava的單繼承特性(每個(gè)代理類都繼承了Proxy類)耳奕,即只能針對接口(多實(shí)現(xiàn))創(chuàng)建代理類绑青,不能針對類創(chuàng)建代理類。
4. JDK動態(tài)代理源碼解析
類的完整加載生命周期
- Java源文件-編譯
- Java字節(jié)碼文件-類加載(來源:磁盤或者內(nèi)存)
- Class對象-實(shí)例化
- 實(shí)例對象
-
卸載
通過調(diào)試模式我們發(fā)現(xiàn)屋群,動態(tài)代理里闸婴,代理類的類名是這樣的:
com.sun.proxy.$Proxy0
這個(gè)代理類為何是這個(gè)名字?它是如何執(zhí)行真實(shí)對象的相關(guān)方法呢芍躏?我們在java文件編譯后的目錄里其實(shí)找不到這個(gè)名為$Proxy0的class文件的掠拳。
觀察源碼的執(zhí)行流程發(fā)現(xiàn),這個(gè)動態(tài)代理的字節(jié)碼文件是在內(nèi)存中創(chuàng)建的纸肉,與創(chuàng)建對象有關(guān)的代碼主要有:
com/jay/java/DynamicProxy/test/ProxyMainTest.java.Demo2()
//創(chuàng)建真實(shí)對象實(shí)例
Subject realSubject = new RealSubject();
//將真實(shí)對象傳入代理生產(chǎn)工廠類中
DynamicProxySubjectFactory proxySubjectFactory = new DynamicProxySubjectFactory(realSubject);
//獲取jdk動態(tài)生成的代理類
Subject proxySubject = (Subject) proxySubjectFactory.getProxyInstance();
com/jay/java/DynamicProxy/test/ProxyMainTest.DynamicProxySubjectFactory.getProxyInstance()
//獲取代理對象
Object proxyInstance = Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),realSubject.getClass().getInterfaces(),this);
return proxyInstance;
/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/src.zip!/java/lang/reflect/Proxy.java.newProxyInstance()
//查找或生成指定的代理類。
Class<?> cl = getProxyClass0(loader, intfs);
//代理類構(gòu)造函數(shù)的參數(shù)類型
private static final Class<?>[] constructorParams = { InvocationHandler.class };
//獲取代理對象構(gòu)造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
//返回代理對象
return cons.newInstance(new Object[]{h});
/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/src.zip!/java/lang/reflect/Proxy.java.getProxyClass0()
//代理類的緩存
private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory
//如果代理類是由實(shí)現(xiàn)的加載器定義的
//給定的接口存在喊熟,這將簡單地返回緩存的副本;
//否則柏肪,它將通過ProxyClassFactory創(chuàng)建代理類
return proxyClassCache.get(loader, interfaces);
/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/src.zip!/java/lang/reflect/WeakCache.java.get()
//創(chuàng)建subKey并檢索可用的的Supplier<V>存儲
//subKey來自valuesMap
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);
/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/src.zip!/java/lang/reflect/Proxy.ProxyClassFactory.java.apply()
//為要生成的代理類選擇一個(gè)名稱。
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
//生成指定的代理類芥牌。
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
//通過生成的字節(jié)碼文件生成動態(tài)代理類
return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
//native 代碼暫時(shí)不能向下追蹤
private static native Class<?> defineClass0(ClassLoader loader, String name, byte[] b, int off, int len);
/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/rt.jar!/sun/misc/ProxyGenerator.class.generateProxyClass()
//由IntelliJ IDEA從.class文件重新創(chuàng)建的源代碼
//(由Fernflower反編譯器提供動力)
com/jay/java/DynamicProxy/test/ProxyMainTest.java.Demo2()
//執(zhí)行代理對象的方法
proxySubject.request("動態(tài)代理類調(diào)用了");
com.sun.proxy.$Proxy0.request()
//通過InvocationHandler接口回掉出來代理對象的方法調(diào)用
super.h.invoke(this, m3, new Object[]{var1});
com/jay/java/DynamicProxy/test/ProxyMainTest.DynamicProxySubjectFactory.invoke()
//反射執(zhí)行真實(shí)對象對應(yīng)的method
Object result = method.invoke(realSubject, args);
return result;
以上就是創(chuàng)建代理對象以及代理對象執(zhí)行真實(shí)對的的方法的大體流程
雖然我們無法追溯到代理類的字節(jié)碼是如何生成的烦味,但是我們可以取巧利用jdk源碼將字節(jié)碼通過文件流輸出到本地,方法如下:
/**
* 模擬動態(tài)生成代理類并輸出到指定路徑,路徑不能包含中文字符
* /DevelopNote/Java/build/classes/java/main/com/jay/java/DynamicProxy/$Proxy0.class
*/
private static void generateProxyClassFile(Class clazz, String proxyName) {
/*ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, new Class[]{clazz});
String paths = clazz.getResource(".").getPath();
System.out.println("生成地址:" + paths);
System.out.println("代理類命:" + proxyName);
FileOutputStream out = null;
try {
out = new FileOutputStream(paths + proxyName + ".class");
out.write(proxyClassFile);
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
最終我們可以這個(gè)看到這個(gè)神秘的Proxy0.class
里面的代碼如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import com.jay.java.DynamicProxy.test.ProxyMainTest.RealSubject;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements RealSubject {
private static Method m1;
private static Method m9;
private static Method m2;
private static Method m3;
private static Method m4;
private static Method m6;
private static Method m5;
private static Method m8;
private static Method m10;
private static Method m0;
private static Method m7;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void notify() throws {
try {
super.h.invoke(this, m9, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String request(String var1) throws {
try {
return (String)super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final int response(int var1) throws {
try {
return (Integer)super.h.invoke(this, m4, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void wait(long var1) throws InterruptedException {
try {
super.h.invoke(this, m6, new Object[]{var1});
} catch (RuntimeException | InterruptedException | Error var4) {
throw var4;
} catch (Throwable var5) {
throw new UndeclaredThrowableException(var5);
}
}
public final void wait(long var1, int var3) throws InterruptedException {
try {
super.h.invoke(this, m5, new Object[]{var1, var3});
} catch (RuntimeException | InterruptedException | Error var5) {
throw var5;
} catch (Throwable var6) {
throw new UndeclaredThrowableException(var6);
}
}
public final Class getClass() throws {
try {
return (Class)super.h.invoke(this, m8, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void notifyAll() throws {
try {
super.h.invoke(this, m10, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void wait() throws InterruptedException {
try {
super.h.invoke(this, m7, (Object[])null);
} catch (RuntimeException | InterruptedException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m9 = Class.forName("com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject").getMethod("notify");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject").getMethod("request", Class.forName("java.lang.String"));
m4 = Class.forName("com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject").getMethod("response", Integer.TYPE);
m6 = Class.forName("com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject").getMethod("wait", Long.TYPE);
m5 = Class.forName("com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject").getMethod("wait", Long.TYPE, Integer.TYPE);
m8 = Class.forName("com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject").getMethod("getClass");
m10 = Class.forName("com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject").getMethod("notifyAll");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m7 = Class.forName("com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject").getMethod("wait");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
測試代碼:
/**
* Demo3:JDK動態(tài)代理原理分析
* 通過調(diào)試模式我們發(fā)現(xiàn)谬俄,動態(tài)代理里柏靶,代理類的類名是這樣的:
* {$Proxy0@505}com.jay.java.動態(tài)代理.ProxyMainTest$RealSubject@3c679bde
* 這個(gè)代理類為何是這個(gè)名字?它是如何執(zhí)行被代理對象的相關(guān)方法呢溃论?我們在java文件編譯后的目錄里其實(shí)找不到這個(gè)名為$Proxy0的class文件的屎蜓。
* 觀察Proxy.newProxyInstance方法,與創(chuàng)建對象有關(guān)的代碼主要有:
* 1,獲得代理類的class對象:
* Class<?> cl = getProxyClass0(loader, intfs);
* 2,獲得代理類的構(gòu)造器:
* final Constructor<?> cons = cl.getConstructor(constructorParams);
* 3,創(chuàng)建代理類的實(shí)例
* return cons.newInstance(new Object[]{h});
* <p>
* 看來其中的關(guān)鍵點(diǎn)就是如何獲得代理類的class對象钥勋,我們進(jìn)入getProxyClass0方法炬转,進(jìn)而進(jìn)入proxyClassCache.get方法,通過這個(gè)這個(gè)方法所在的類名算灸,我們可以推測扼劈,JDK內(nèi)部使用了某種機(jī)制緩存了我們的代理類的class對象,同時(shí)get方法接受的參數(shù)是被代理類的類加載器和類實(shí)現(xiàn)的的接口菲驴。
* 在這個(gè)get方法中荐吵,除去和緩存相關(guān)的操作,同時(shí)用到了被代理類的類加載器和類實(shí)現(xiàn)的的接口這兩個(gè)參數(shù)的是
* Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
* Generate the specified proxy class.
* byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName,interfaces,accessFlags);
* String proxyName = proxyPkg + proxyClassNamePrefix + num;
* return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
* 而最終生成代理類的class對象是defineClass0方法赊瞬,但是這個(gè)方法是個(gè)native方法先煎,所以我們不去也無法深究它,但是通過這個(gè)方法的參數(shù)我們可以明顯看到它接收了上面所生成的byte數(shù)組森逮。
* private static native Class<?> defineClass0(ClassLoader loader, String name, byte[] b, int off, int len);
*/
private static void Demo3() {
System.out.println("-----Demo3-----\n\n");
System.out.println("-----JDK動態(tài)代理原理分析-----");
Subject realSubject = new RealSubject();
//將被代理對象傳入代理生成工廠類
DynamicProxySubjectFactory proxySubjectFactory = new DynamicProxySubjectFactory(realSubject);
//獲取jdk動態(tài)生成的代理類
Subject proxySubject = (Subject) proxySubjectFactory.getProxyInstance();
//模擬動態(tài)生成代理類字節(jié)碼文件并輸出到本地榨婆,
generateProxyClassFile(realSubject.getClass(), proxySubject.getClass().getSimpleName());
//獲取代理類的方法
Method[] methods = proxySubject.getClass().getMethods();
//打印方法名稱
for (Method method : methods) {
System.out.println(method.getName());
}
}
運(yùn)行結(jié)果:
-----Demo3-----
-----JDK動態(tài)代理原理分析-----
---------------------getProxyInstance-----------------------
---getProxyInstance-realSubject.getClass()=
class com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject
---getProxyInstance-realSubject.getClass().getClassLoader()=
sun.misc.Launcher$AppClassLoader@18b4aac2
---getProxyInstance-realSubject.getClass().getInterfaces()[0]=
interface com.jay.java.DynamicProxy.test.ProxyMainTest$Subject
interface com.jay.java.DynamicProxy.test.ProxyMainTest$Subject2
---getProxyInstance-代理對象proxyInstance=
com.sun.proxy.$Proxy0
生成地址:/Users/xuejiewang/AndroidStudioProjects/Jay/DevelopNote/Java/build/classes/java/main/com/jay/java/DynamicProxy/test/
代理類命:$Proxy0
equals
toString
hashCode
request
response
isProxyClass
newProxyInstance
getProxyClass
getInvocationHandler
wait
wait
wait
getClass
notify
notifyAll
Process finished with exit code 0
4. cglib動態(tài)代理
- jdk動態(tài)代理:代理所有“實(shí)現(xiàn)抽象對象接口接口”的真實(shí)對象
- cglib動態(tài)代理:代理任意一個(gè)目標(biāo)類,但對final類和方法無法代理
- 不同點(diǎn):jdk動態(tài)代理的目標(biāo)類必須實(shí)現(xiàn)的有接口,因?yàn)樵谡{(diào)用Proxy.newProxyInstance()的時(shí)候需要傳入目標(biāo)類的接口類褒侧。而cglib不做此限制良风。
三,實(shí)踐出真知
測試代碼:Github
1. Retrofit中對動態(tài)代理的使用
Retrofit的一般使用如下:
/**
* 數(shù)據(jù)接受實(shí)體類
*/
private class Repo {
private String id;
private String name;
private String description;
private String url;
@Override
public String toString() {
return "Repo{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", description='" + description + '\'' +
", url='" + url + '\'' +
'}';
}
}
/**
* Retrofit將您的HTTP API轉(zhuǎn)換為Java接口闷供。
* 抽象接口對象
*/
public interface GitHubService {
//https://api.github.com/users/Jay-Droid/repos
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
測試代碼:
/**
* Demo1:Retrofit中對動態(tài)代理的使用
* https://github.com/square/retrofit/blob/master/retrofit/src/main/java/retrofit2/Retrofit.java
* <p>
* retrofit.create(GitHubService.class);
*/
private static void Demo1() {
System.out.println("-----Demo1-----\n\n");
System.out.println("-----Retrofit中對動態(tài)代理的使用-----");
//Retrofit類生成GitHubService接口的實(shí)現(xiàn)烟央。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
GitHubService service = retrofit.create(GitHubService.class);
//從創(chuàng)建的GitHubService進(jìn)行的每次調(diào)用都可以向遠(yuǎn)程Web服務(wù)器發(fā)出同步或異步HTTP請求。
Call<List<Repo>> repos = service.listRepos("Jay-Droid");
repos.enqueue(new Callback<List<Repo>>() {
@Override
public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
System.out.println("-----onResponse-----");
//Repo{id='76014709', name='ActivityOptions', description='android ActivityOption 中的五種轉(zhuǎn)場動畫', url='https://api.github.com/repos/Jay-Droid/ActivityOptions'}
System.out.println(response.body().get(0).toString());
}
@Override
public void onFailure(Call<List<Repo>> call, Throwable throwable) {
System.out.println("-----onFailure-----");
System.out.println(throwable.getLocalizedMessage());
}
});
}
運(yùn)行結(jié)果:
-----Demo1-----
-----Retrofit中對動態(tài)代理的使用-----
-----onResponse-----
Repo{id='76014709', name='ActivityOptions', description='android ActivityOption 中的五種轉(zhuǎn)場動畫', url='https://api.github.com/repos/Jay-Droid/ActivityOptions'}
Process finished with exit code 0
/**
* 利用動態(tài)代理技術(shù)生成代理對象
* retrofit.create()
*/
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
return (T) Proxy.newProxyInstance(
service.getClassLoader(),
new Class<?>[] { service },
InvocationHandler內(nèi)部實(shí)現(xiàn)類
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override public @Nullable Object invoke(Object proxy, Method method,
@Nullable Object[] args) throws Throwable {
// 如果方法是來自對象的方法歪脏,則不做代理
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
判斷是否是默認(rèn)方法疑俭,這是1.8新增的內(nèi)容
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}
2. 動態(tài)代理在組件化路由框架中的實(shí)踐
/**
* Demo2:動態(tài)代理組件化路由框架實(shí)戰(zhàn) 關(guān)鍵詞:URI,Scheme婿失,注解钞艇,動態(tài)代理
* 詳見:com.jay.develop.java.dynamic_proxy.DynamicProxyActivity 實(shí)現(xiàn)步驟:
* 1.定義兩個(gè)注解參數(shù),一個(gè)標(biāo)示URI注解豪硅,一個(gè)標(biāo)示參數(shù) @Documented @Target(METHOD) @Retention(RUNTIME) public @interface
* RouterUri { String routerUri() default ""; } @Documented @Target(PARAMETER) @Retention(RUNTIME)
* public @interface RouterParam { String value() default "";
*
* <p>}
*/
private static void Demo2() {
System.out.println("-----Demo2-----\n\n");
System.out.println("-----動態(tài)代理組件化路由框架實(shí)戰(zhàn) -----");
/**
* 1.定義兩個(gè)注解參數(shù)哩照,一個(gè)標(biāo)示URI注解,一個(gè)標(biāo)示參數(shù)
*
//請求Url地址,在AndroidManifest文件中為每個(gè)Activity添加scheme
@RouterUri(routerUri = "dv://com.jay.develop/router")
void jumpToDynamicProxyPage(
@RouterParam("info") String info,
@RouterParam("des") String des
);
@Documented
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface RouterParam {
String value() default "";
}
2.定義一個(gè)URI協(xié)議接口
public interface IRouterUri {
//請求Url地址,在AndroidManifest文件中為每個(gè)Activity添加scheme
@RouterUri(routerUri = "dv://com.jay.develop/router")
void jumpToDynamicProxyPage(
@RouterParam("info") String info,
@RouterParam("des") String des
);
}
3.定義一個(gè)單例懒浮,內(nèi)部通過動態(tài)代理機(jī)制實(shí)現(xiàn)跳轉(zhuǎn)
XLRouter.class
4.)在進(jìn)行XLRouter初始化
XLRouter.initXLRouter(this);
5.調(diào)用方式
XLRouter.routerUri().jumpToGoodsDetail("1000110002","goods des");
**/
}
具體實(shí)現(xiàn):
/**
* 注解參數(shù),標(biāo)示參數(shù)注解
*
* @author wangxuejie
* @version 1.0
* @date 2019-12-02 18:19
*/
@Documented
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface RouterParam {
String value() default "";
}
/**
* 注解參數(shù),標(biāo)示URI注解
* @author wangxuejie
* @version 1.0
* @date 2019-12-02 18:20
*/
@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface RouterUri {
String routerUri() default "";
}
/**
* 定義一個(gè)URI協(xié)議接口
*
* @author wangxuejie
* @version 1.0
* @date 2019-12-02 18:18
*/
public interface IRouterUri {
//請求Url地址,在AndroidManifest文件中為每個(gè)Activity添加scheme
@RouterUri(routerUri = "dv://com.jay.develop/router")
void jumpToDynamicProxyPage(
@RouterParam("info") String info,
@RouterParam("des") String des
);
}
/**
* 內(nèi)部通過動態(tài)代理機(jī)制實(shí)現(xiàn)跳轉(zhuǎn)
* <p>
* 參考地址: https://www.cnblogs.com/whoislcj/p/5860138.html
*
* @author wangxuejie
* @version 1.0
* @date 2019-12-02 18:15
*/
public class XLRouter {
private final static String TAG = XLRouter.class.getSimpleName();
private static IRouterUri mRouterUri;
private static Context mContext;
/**
* 初始化
*/
public static void initXLRouter(Context context) {
mContext = context.getApplicationContext();
mRouterUri = create(IRouterUri.class);
}
/**
* 返回Api
*/
public static IRouterUri routerUri() {
if (mRouterUri == null) {
throw new IllegalStateException("XLRouter還未初始化");
}
return mRouterUri;
}
/**
* 通過動態(tài)代理生成IRouterUri接口的實(shí)例對象并解析注解參數(shù)
*
* @param aClass IRouterUri Class對象
* @return 真實(shí)對象的代理對象
*/
private static IRouterUri create(Class aClass) {
return (IRouterUri) Proxy.newProxyInstance(aClass.getClassLoader(), new Class[]{aClass},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object... args) throws Throwable {
StringBuilder stringBuilder = new StringBuilder();
//獲取RouterUri注解
RouterUri reqUrl = method.getAnnotation(RouterUri.class);
Log.e(TAG, "IReqApi---reqUrl->" + reqUrl.routerUri());
stringBuilder.append(reqUrl.routerUri());
//Type[] parameterTypes = method.getGenericParameterTypes();//獲取注解參數(shù)類型
//拿到參數(shù)注解
Annotation[][] parameterAnnotationsArray = method.getParameterAnnotations();
//Annotation[] annotation = method.getDeclaredAnnotations();
/*
定義的路由接口
@RouterUri(routerUri = "dv://com.jay.develop/router")
void jumpToDynamicProxyPage(
@RouterParam("info") String info,
@RouterParam("des") String des
);
*/
int pos = 0;
for (int i = 0; i < parameterAnnotationsArray.length; i++) {
Annotation[] annotations = parameterAnnotationsArray[i];
if (annotations != null && annotations.length != 0) {
if (pos == 0) {
stringBuilder.append("?");
} else {
stringBuilder.append("&");
}
pos++;
RouterParam reqParam = (RouterParam) annotations[0];
stringBuilder.append(reqParam.value());
stringBuilder.append("=");
stringBuilder.append(args[i]);
Log.e(TAG, "reqParam---reqParam->" + reqParam.value() + "=" + args[i]);
}
}
Log.e(TAG, "stringBuilder.toString()->" + stringBuilder.toString());
//dv://com.jay.develop/router?info=我是通過XLRouter路由框架跳轉(zhuǎn)的&des=描述
//下面就可以執(zhí)行相應(yīng)的跳轉(zhuǎn)操作
openRouterUri(stringBuilder.toString());
return null;
}
});
}
/**
* 通過uri跳轉(zhuǎn)指定頁面
*
* @param url deeplink
*/
private static void openRouterUri(String url) {
PackageManager packageManager = mContext.getPackageManager();
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
List activities = packageManager.queryIntentActivities(intent, 0);
boolean isValid = !activities.isEmpty();
if (isValid) {
mContext.startActivity(intent);
}
}
/**
* 4.)在進(jìn)行XLRouter初始化
*
* XLRouter.initXLRouter(this);
*
*
* 5.調(diào)用方式
*
* XLRouter.routerUri().jumpToGoodsDetail("1000110002","goods des");
*/
}
測試代碼:
//A Activity中調(diào)用
XLRouter.routerUri().jumpToDynamicProxyPage("我是通過XLRouter路由框架跳轉(zhuǎn)的", "我是描述");
//B Activity中注冊deeplink
<activity android:name=".java.dynamic_proxy.DynamicProxyActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!--dv://com.jay.develop/router-->
<data
android:host="com.jay.develop"
android:path="/router"
android:scheme="dv" />
</intent-filter>
</activity>
/**
* 動態(tài)代理組件化路由框架實(shí)戰(zhàn)
* URI飘弧,Scheme,注解,動態(tài)代理
* 通過該方法打開該頁面
* XLRouter.routerUri().jumpToDynamicProxyPage("我是通過XLRouter路由框架跳轉(zhuǎn)的", "我是描述");
*/
class DynamicProxyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_dynamic_proxy)
getDataFromBrowser()
}
/**
* 從deep link中獲取數(shù)據(jù)
*/
private fun getDataFromBrowser() {
val data = intent.data
try {
tv_info.text =
"Uri :" + data!!.toString() + "\n" +
"Scheme: " + data!!.scheme + "\n" +
"host: " + data!!.host + "\n" +
"path: " + data!!.path
} catch (e: Exception) {
e.printStackTrace()
}
}
}