1.前言
我們知道不管靜態(tài)代理還是動態(tài)代理,實際上都是對于源對象的一種控制,通過代理對象來間接訪問目標對象熄捍。但是靜態(tài)代理有以下兩個問題
靜態(tài)代理存在的問題是:
1)一旦接口新增或者修改,那么代理對象和被代理對象就得去適配修改
2)靜態(tài)代理是在代碼編寫時赏半,去生成的,class文件必然會造成類爆炸的風險
有沒有方案解決以上的問題呢?顯然是有的,那就是動態(tài)代理饲握。動態(tài)代理私杜,顧名思義,就是在運行時去動態(tài)生成代理救欧,我們下面分別說兩種實現(xiàn)方式,分別為JDK和Cglib锣光。
2.兩種實現(xiàn)方案
2.1 實現(xiàn)方案--JDK動態(tài)代理
如上面所說笆怠,一般被代理的對象會實現(xiàn)某個接口,我們是否可以借用代理模式的這個特點做點文章呢誊爹?
JDK給我們提供了一些API蹬刷,可以實現(xiàn)動態(tài)代理,InvocationHandler和Proxy.newProxyInstance來實現(xiàn)频丘。
動態(tài)代理類:
package com.itbird.design.proxy.demo.dynamic.v1;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 代理對象
* Created by itbird on 2022/7/4
*/
public class ProxyObject implements InvocationHandler {
Object proxy;
public ProxyObject(Object proxy) {
this.proxy = proxy;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(proxy, args);
}
}
代碼很簡單办成,就是實現(xiàn)InvocationHandler接口,重寫invoke方法搂漠,這里有一個關鍵的構造函數(shù)迂卢,就是要入?yún)⒃磳ο蟆K晕覀兛匆幌抡{(diào)用桐汤。
package com.itbird.design.proxy.demo.dynamic.v1;
import java.lang.reflect.Proxy;
/**
* Created by itbird on 2022/7/4
*/
public class Client {
public void main() {
SourceObject sourceObject = new SourceObject();
// 返回的是 IBank 的一個實例對象而克,這個對象是由 Java 給我們創(chuàng)建的 ,調(diào)用的是 jni,通過反射+classloader加載
IObject proxy = (IObject) Proxy.newProxyInstance(IObject.class.getClassLoader(), // ClassLoader
new Class[]{IObject.class}, //目標接口
new ProxyObject(sourceObject)); //替換代理
proxy.methodA();
}
}
是否很簡單怔毛,如果這時接口新增或者修改员萍,大家發(fā)現(xiàn)沒有,不再需要去修改代理類拣度。因為其本質(zhì)是碎绎,通過反射+classloader去實現(xiàn)的,所以不依賴于對象抗果。
但是依然有一個問題筋帖,就是Proxy.newProxyInstance使用上有一個弊端,就是必須面向接口窖张,如果源對象也就是被代理對象幕随,如果沒有實現(xiàn)某個接口,這時不就GG了宿接。該怎么做呢赘淮?不要著急,Cglib可以幫我們解決這個問題睦霎。
2.2 實現(xiàn)方案--Cglib
我們知道一個對象在內(nèi)存中存儲梢卸,一般就是在棧或者堆上副女,那是否有一種方法或者組件蛤高,可以直接從內(nèi)存copy這個對象,實現(xiàn)深克隆,也就是在內(nèi)存中在copy一個出來戴陡。
Cglib代理模式的基本介紹
1) JDK動態(tài)代理模式要求目標對象是實現(xiàn)一個接口,但是有時候目標對象只是一個單獨的對象,并沒有實現(xiàn)任何的接口,這個時候可使用目標對象子類來實現(xiàn)代理-這就是Cglib代理
2)Cglib代理也叫作子類代理,它是在內(nèi)存中構建一個子類對象從而實現(xiàn)對目標對象功能擴展
3)Cglib是一個強大的高性能的代碼生成包,它可以在運行期擴展java類與實現(xiàn)java接口.它廣泛的被許多AOP的框架使用,例如Spring AOP塞绿,實現(xiàn)方法攔截
4)Cglib包的底層是通過使用字節(jié)碼處理框架ASM來轉換字節(jié)碼并生成新的類
5)Cglib由于是基于字節(jié)碼的,顯然這時android就不能使用了恤批,因為android是dex文件异吻,這怎么辦?沒關系喜庞,有 dexmaker 和 cglib-for-android 庫诀浪。
Cglib代理如何實現(xiàn)呢?
package com.itbird.design.proxy.demo.dynamic.v2;
/**
* 原始對象,預對其訪問晰房,加以控制的對象求摇,沒有實現(xiàn)接口
* Created by itbird on 2022/7/4
*/
public class SourceObject {
public void methodA() {
//TODO 具體的實現(xiàn)
}
public void methodB() {
//TODO 具體的實現(xiàn)
}
public void methodC() {
//TODO 具體的實現(xiàn)
}
public void methodD() {
//TODO 具體的實現(xiàn)
}
public void methodE() {
//TODO 具體的實現(xiàn)
}
}
第二步:代碼實現(xiàn),首先使用Cglib實現(xiàn)方法攔截
package com.itbird.design.proxy.demo.dynamic.v2;
import leo.android.cglib.proxy.MethodInterceptor;
import leo.android.cglib.proxy.MethodProxy;
/**
* 代理對象
* Created by itbird on 2022/7/4
*/
public class ProxyMethodInterceptor implements MethodInterceptor {
/**
* 重寫方法攔截在方法前和方法后加入業(yè)務
* Object obj為目標對象
* Method method為目標方法
* Object[] params 為參數(shù)嫉你,
* MethodProxy proxy CGlib方法代理對象
*/
@Override
public Object intercept(Object o, Object[] objects, MethodProxy methodProxy) throws Exception {
//參數(shù):Object為由CGLib動態(tài)生成的代理類實例月帝,Method為上文中實體類所調(diào)用的被代理的方法引用,Object[]為參數(shù)值列表幽污,MethodProxy為生成的代理類對方法的代理引用嚷辅。
//返回:從代理實例的方法調(diào)用返回的值。
//其中距误,proxy.invokeSuper(obj,arg) 調(diào)用代理類實例上的proxy方法的父類方法(即實體類TargetObject中對應的方法)
System.out.println("調(diào)用前");
Object result = methodProxy.invokeSuper(o,objects);
System.out.println(" 調(diào)用后"+result);
return result;
}
}
第三步簸搞,調(diào)用
package com.itbird.design.proxy.demo.dynamic.v2;
import android.content.Context;
import leo.android.cglib.proxy.Enhancer;
/**
* Created by itbird on 2022/7/4
*/
public class Client {
public void main(Context context) {
//Enhancer類是CGLib中的一個字節(jié)碼增強器
Enhancer enhancer = new Enhancer(context);
//將被代理類TargetObject設置成父類,然后設置攔截器TargetInterceptor
enhancer.setSuperclass(SourceObject.class);
enhancer.setInterceptor(new ProxyMethodInterceptor());
//執(zhí)行enhancer.create()動態(tài)生成一個代理類准潭,并從Object強制轉型成父類型TargetObject
SourceObject object = (SourceObject) enhancer.create();
//在代理類上調(diào)用方法
object.methodA();
}
}
大家發(fā)現(xiàn)趁俊,此時實現(xiàn)了源對象的方法調(diào)用。