這一次徹底搞懂JDK動態(tài)代理
動態(tài)代理 V.S 靜態(tài)代理
Proxy類的代碼被固定下來,不會因為業(yè)務(wù)的逐漸龐大而龐大
可以實現(xiàn)AOP編程,這是靜態(tài)代理無法實現(xiàn)的
解耦,如果用在web業(yè)務(wù)下,可以實現(xiàn)數(shù)據(jù)層和業(yè)務(wù)層的分離
動態(tài)代理的優(yōu)勢就是實現(xiàn)無侵入式的代碼擴展。
靜態(tài)代理這個模式本身有個大問題,若類方法數(shù)量越來越多的時候,代理類的代碼量十分龐大的。所以引入動態(tài)代理
動態(tài)代理
Java中動態(tài)代理的實現(xiàn)的關(guān)鍵:
- Proxy
- InvocationHandler
InvocationHandler#invoke
- method 調(diào)用的方法,即需要執(zhí)行的方法
- args 方法的參數(shù)
- proxy 代理類的實例 圖片
JDK動態(tài)代理
JDK動態(tài)代理模式里有個攔截器,在JDK中,只要實現(xiàn)了InvocationHandler接口的類就是一個攔截器類。攔截器的作用:控制目標對象的目標方法的執(zhí)行。
攔截器的具體操作步驟:
1.引入類
目標類和一些擴展方法相關(guān)的類
2.賦值
調(diào)用構(gòu)造器,給相關(guān)對象賦值
3.合并邏輯處理
在invoke方法中把所有的邏輯結(jié)合在一起。最終決定目標方法是否被調(diào)用
示例
思考如下問題:
代理對象由誰產(chǎn)生
JVM,不像靜態(tài)代理,我們得自己new個代理對象。
代理對象實現(xiàn)了什么接口
實現(xiàn)的接口是目標對象實現(xiàn)的接口。同靜態(tài)代理中代理對象實現(xiàn)的接口。那個繼承關(guān)系圖還是相同的。代理對象和目標對象都實現(xiàn)一個共同的接口。就是這個接口。所以Proxy.newProxyInstance()方法返回的類型就是這個接口類型。
代理對象的方法體是什么
代理對象的方法體中的內(nèi)容就是攔截器中invoke方法中的內(nèi)容。
所有代理對象的處理邏輯,控制是否執(zhí)行目標對象的目標方法。都是在這個方法里面處理的。
攔截器中的invoke方法中的method參數(shù)是在什么時候賦值的
在客戶端,代理對象調(diào)用目標方法的時候,此實例中為:
- proxyObj.business();
實際上進入的是攔截器中的invoke方法,這時攔截器中的invoke方法中的method參數(shù)會被賦值。
為啥這叫JDK動態(tài)代理
因為該動態(tài)代理對象是用JDK相關(guān)代碼生成。
很多同學對動態(tài)代理一直很迷糊,在于理解錯了
- proxyObj.business();
- $Proxy0
沒有發(fā)現(xiàn)這個 proxyObj 和 Proxy 類之間的聯(lián)系,一直好奇
最后調(diào)用的business()是怎么和 invoke() 聯(lián)系上的?
invoke又怎么知道business的存在?
因為大多同學不知道 $Proxy0 類,看看下面的 $Proxy0 源碼,相信你完全可以理解動態(tài)代理了。
我們雖然沒有顯式調(diào)用invoke,但該方法確實被執(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類實現(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類 實現(xiàn)了interfaces的接口,并繼承了Proxy類
- 實例化$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實例強轉(zhuǎn)成TargetInterface,并將引用賦給TargetInterface。當執(zhí)行proxyObj.business(),就調(diào)用了$Proxy0類中的business()方法,進而調(diào)用父類Proxy中的h的invoke()方法。即InvocationHandler.invoke()。
最后提醒Proxy#getProxyClass返回的是Proxy的Class類,而非很同學想當然認為的“被代理類的Class類”!
本文轉(zhuǎn)載自微信公眾號「JavaEdge」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系JavaEdge公眾號。