這一次徹底搞懂JDK動(dòng)態(tài)代理
動(dòng)態(tài)代理 V.S 靜態(tài)代理
Proxy類的代碼被固定下來,不會(huì)因?yàn)闃I(yè)務(wù)的逐漸龐大而龐大
可以實(shí)現(xiàn)AOP編程,這是靜態(tài)代理無法實(shí)現(xiàn)的
解耦,如果用在web業(yè)務(wù)下,可以實(shí)現(xiàn)數(shù)據(jù)層和業(yè)務(wù)層的分離
動(dòng)態(tài)代理的優(yōu)勢(shì)就是實(shí)現(xiàn)無侵入式的代碼擴(kuò)展。
靜態(tài)代理這個(gè)模式本身有個(gè)大問題,若類方法數(shù)量越來越多的時(shí)候,代理類的代碼量十分龐大的。所以引入動(dòng)態(tài)代理
動(dòng)態(tài)代理
Java中動(dòng)態(tài)代理的實(shí)現(xiàn)的關(guān)鍵:
- Proxy
- InvocationHandler
InvocationHandler#invoke
- method 調(diào)用的方法,即需要執(zhí)行的方法
- args 方法的參數(shù)
- proxy 代理類的實(shí)例 圖片
JDK動(dòng)態(tài)代理
JDK動(dòng)態(tài)代理模式里有個(gè)攔截器,在JDK中,只要實(shí)現(xiàn)了InvocationHandler接口的類就是一個(gè)攔截器類。攔截器的作用:控制目標(biāo)對(duì)象的目標(biāo)方法的執(zhí)行。
攔截器的具體操作步驟:
1.引入類
目標(biāo)類和一些擴(kuò)展方法相關(guān)的類
2.賦值
調(diào)用構(gòu)造器,給相關(guān)對(duì)象賦值
3.合并邏輯處理
在invoke方法中把所有的邏輯結(jié)合在一起。最終決定目標(biāo)方法是否被調(diào)用
示例
思考如下問題:
代理對(duì)象由誰產(chǎn)生
JVM,不像靜態(tài)代理,我們得自己new個(gè)代理對(duì)象。
代理對(duì)象實(shí)現(xiàn)了什么接口
實(shí)現(xiàn)的接口是目標(biāo)對(duì)象實(shí)現(xiàn)的接口。同靜態(tài)代理中代理對(duì)象實(shí)現(xiàn)的接口。那個(gè)繼承關(guān)系圖還是相同的。代理對(duì)象和目標(biāo)對(duì)象都實(shí)現(xiàn)一個(gè)共同的接口。就是這個(gè)接口。所以Proxy.newProxyInstance()方法返回的類型就是這個(gè)接口類型。
代理對(duì)象的方法體是什么
代理對(duì)象的方法體中的內(nèi)容就是攔截器中invoke方法中的內(nèi)容。
所有代理對(duì)象的處理邏輯,控制是否執(zhí)行目標(biāo)對(duì)象的目標(biāo)方法。都是在這個(gè)方法里面處理的。
攔截器中的invoke方法中的method參數(shù)是在什么時(shí)候賦值的
在客戶端,代理對(duì)象調(diào)用目標(biāo)方法的時(shí)候,此實(shí)例中為:
- proxyObj.business();
實(shí)際上進(jìn)入的是攔截器中的invoke方法,這時(shí)攔截器中的invoke方法中的method參數(shù)會(huì)被賦值。
為啥這叫JDK動(dòng)態(tài)代理
因?yàn)樵搫?dòng)態(tài)代理對(duì)象是用JDK相關(guān)代碼生成。
很多同學(xué)對(duì)動(dòng)態(tài)代理一直很迷糊,在于理解錯(cuò)了
- proxyObj.business();
- $Proxy0
沒有發(fā)現(xiàn)這個(gè) proxyObj 和 Proxy 類之間的聯(lián)系,一直好奇
最后調(diào)用的business()是怎么和 invoke() 聯(lián)系上的?
invoke又怎么知道business的存在?
因?yàn)榇蠖嗤瑢W(xué)不知道 $Proxy0 類,看看下面的 $Proxy0 源碼,相信你完全可以理解動(dòng)態(tài)代理了。
我們雖然沒有顯式調(diào)用invoke,但該方法確實(shí)被執(zhí)行了。
可以從newProxyInstance方法作為突破口,我們先來看一下Proxy類中newProxyInstance方法的源代碼:
- public static Object newProxyInstance(ClassLoader loader,
- Class<?>[] interfaces,
- InvocationHandler h) {
- final Class<?>[] intfs = interfaces.clone();
- final SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
- }
- /*
- * 查找或生成指定的代理類
- * 創(chuàng)建代理類$Proxy0
- * $Proxy0類實(shí)現(xiàn)了interfaces的接口,并繼承了Proxy類
- */
- Class<?> cl = getProxyClass0(loader, intfs);
- /*
- * 使用指定的調(diào)用處理程序調(diào)用其構(gòu)造器
- */
- try {
- if (sm != null) {
- checkNewProxyPermission(Reflection.getCallerClass(), cl);
- }
- // 形參為InvocationHandler類型的構(gòu)造器
- final Constructor<?> cons = cl.getConstructor(constructorParams);
- final InvocationHandler ih = h;
- if (!Modifier.isPublic(cl.getModifiers())) {
- AccessController.doPrivileged(new PrivilegedAction<Void>() {
- public Void run() {
- cons.setAccessible(true);
- return null;
- }
- });
- }
- return cons.newInstance(new Object[]{h});
- } ...
- }
Proxy.newProxyInstance 做了什么呢?
- 根據(jù)參數(shù)loader和interfaces調(diào)用方法 getProxyClass(loader, interfaces)創(chuàng)建代理$Proxy0類。$Proxy0類 實(shí)現(xiàn)了interfaces的接口,并繼承了Proxy類
- 實(shí)例化$Proxy0,并在構(gòu)造器把DynamicSubject傳過去,接著$Proxy00調(diào)用父類Proxy的構(gòu)造器,為h賦值
$Proxy0的源碼:
- package com.sun.proxy;
- public final class $Proxy0 extends Proxy implements TargetInterface {
- private static Method m1;
- private static Method m3;
- private static Method m2;
- private static Method m0;
- 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});
- }...
- }
- public final void business() throws {
- try {
- super.h.invoke(this, m3, (Object[])null);
- }...
- }
- public final String toString() throws {
- try {
- return (String)super.h.invoke(this, m2, (Object[])null);
- }...
- }
- public final int hashCode() throws {
- try {
- return (Integer)super.h.invoke(this, m0, (Object[])null);
- }...
- }
- static {
- try {
- m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
- m3 = Class.forName("com.javaedge.design.pattern.structural.proxy.dynamicproxy.jdkdynamicproxy.TargetInterface").getMethod("business");
- m2 = Class.forName("java.lang.Object").getMethod("toString");
- m0 = Class.forName("java.lang.Object").getMethod("hashCode");
- }...
- }
- }
接著把得到的$Proxy0實(shí)例強(qiáng)轉(zhuǎn)成TargetInterface,并將引用賦給TargetInterface。當(dāng)執(zhí)行proxyObj.business(),就調(diào)用了$Proxy0類中的business()方法,進(jìn)而調(diào)用父類Proxy中的h的invoke()方法。即InvocationHandler.invoke()。
最后提醒Proxy#getProxyClass返回的是Proxy的Class類,而非很同學(xué)想當(dāng)然認(rèn)為的“被代理類的Class類”!
本文轉(zhuǎn)載自微信公眾號(hào)「JavaEdge」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系JavaEdge公眾號(hào)。