前言
代理模式是常用的java設(shè)計(jì)模式,他的特征是代理類與委托類有同樣的接口,代理類主要負(fù)責(zé)為委托類預(yù)處理消息千劈、過濾消息、把消息轉(zhuǎn)發(fā)給委托類宝泵,以及事后處理消息等。代理類與委托類之間通常會存在關(guān)聯(lián)關(guān)系忍些,一個(gè)代理類的對象與一個(gè)委托類的對象關(guān)聯(lián)鲁猩,代理類的對象本身并不真正實(shí)現(xiàn)服務(wù),而是通過調(diào)用委托類的對象的相關(guān)方法罢坝,來提供特定的服務(wù)
1、靜態(tài)代理
靜態(tài)代理通常用于對原有業(yè)務(wù)邏輯的擴(kuò)充搅窿。比如持有二方包的某個(gè)類嘁酿,并調(diào)用了其中的某些方法。然后出于某種原因男应,比如記錄日志闹司、打印方法執(zhí)行時(shí)間,但是又不好將這些邏輯寫入二方包的方法里沐飘。所以可以創(chuàng)建一個(gè)代理類實(shí)現(xiàn)和二方方法相同的方法游桩,通過讓代理類持有真實(shí)對象,然后在原代碼中調(diào)用代理類方法耐朴,來達(dá)到添加我們需要業(yè)務(wù)邏輯的目的借卧。
這其實(shí)也就是代理模式的一種實(shí)現(xiàn),通過對真實(shí)對象的封裝筛峭,來實(shí)現(xiàn)擴(kuò)展性铐刘。
一個(gè)典型的代理模式通常有三個(gè)角色,這里稱之為代理三要素
首先定義接口
public interface Action {
public void doSomething();
}
真實(shí)對象
public class RealObject implements Action{
public void doSomething() {
System.out.println("do something");
}
}
代理對象
public class Proxy implements Action {
private Action realObject;
public Proxy(Action realObject) {
this.realObject = realObject;
}
public void doSomething() {
System.out.println("proxy do");
realObject.doSomething();
}
}
運(yùn)行代碼
Proxy proxy = new Proxy(new RealObject());
proxy.doSomething();
這種代理模式也最為簡單影晓,就是通過proxy持有realObject的引用镰吵,并進(jìn)行一層封裝。
靜態(tài)代理的優(yōu)點(diǎn)和缺點(diǎn)
先看看代理模式的優(yōu)點(diǎn): 擴(kuò)展原功能挂签,不侵入原代碼疤祭。
再看看這種代理模式的缺點(diǎn):
假如有這樣一個(gè)需求,有十個(gè)不同的RealObject饵婆,同時(shí)我們要去代理的方法是不同的勺馆,比要代理方法:doSomething、doAnotherThing、doTwoAnotherThing谓传,添加代理前蜈项,原代碼可能是這樣的:
realObject.doSomething();
realObject1.doAnotherThing();
realObject2.doTwoAnother();
2、Java動態(tài)代理
在靜態(tài)代理中可以看到Proxy這個(gè)類做的工作無非就是在RealSubject的工作之前或之后插入其他的業(yè)務(wù)代碼续挟,如果每次都手動去實(shí)現(xiàn)Proxy類會比較繁瑣紧卒,Java中引入了第三個(gè)類InvocationHandler,用來統(tǒng)一映射Proxy方法調(diào)用到RealSubject上诗祸。通過newProxyInstance可以幫助我們動態(tài)生成Proxy類實(shí)例跑芳。
接著看一下核心類和方法
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler invocationHandler)
返回一個(gè)指定接口的代理類實(shí)例,該接口可以將方法調(diào)用指派到指定的調(diào)用處理程序InvocationHandler
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable
對于InvocationHandler直颅,我們需要實(shí)現(xiàn)invoke方法博个,invoke方法根據(jù)代理類傳遞給自己的method參數(shù)來區(qū)分是什么方法
還是以上面栗子來做示范,F(xiàn)ly接口和Run接口不變功偿,需要添加InvocationHandler的實(shí)現(xiàn):
public class ProxyHandler implements InvocationHandler{
private RealObject mRealObject;
public ProxyHandler(RealObject realobject){
mRealObject = realobject;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("invoke" + method.getName());
return method.invoke(mRealObject, args);
}
}
生成代理對象
public static void main(String[] args) {
RealObject realobject = new RealObject();
ClassLoader classLoader = realobject.getClass().getClassLoader();
Class[] interfaces = realobject.getClass().getInterfaces();
ProxyHandler proxyHandler = new ProxyHandler(realobject);
ProxyUtils.generateClassFile(realobject.getClass(), "AnimalProxy");
Object newProxyInstance = Proxy.newProxyInstance(classLoader, interfaces, proxyHandler);
Action action = (Action)newProxyInstance;
action.doSomething;
}
可以看出來核心代碼就是
Proxy.newProxyInstance(classLoader, interfaces, proxyHandler);
我們分別來講解下這三個(gè)參數(shù)分別是什么作用
classLoader:類加載器盆佣,我們手動寫的都是java文件,需要編譯成class文件械荷,這個(gè)是遵循JVM規(guī)范的二進(jìn)制文件共耍,然后通過classLoader將class文件加載進(jìn)內(nèi)存,生成我們需要的class對象吨瞎,這個(gè)class對象通過反射就可以拿到類的所有信息痹兜。在這邊的作用其實(shí)就是將Java動態(tài)生成的class文件進(jìn)行加載得到動態(tài)代理的class對象,以便后面其他操作颤诀。
interfaces:這個(gè)就是接口字旭,可以看出無論代理或者RealSubject都是實(shí)現(xiàn)同樣的接口,Java替我們動態(tài)生成的class文件中的方法其實(shí)就是接口中的方法崖叫。這個(gè)其實(shí)也是Java動態(tài)代理的缺點(diǎn)遗淳,即使RealSubject中聲明的方法,但是接口中沒有聲明該方法归露,那么在生成的代理中就沒有洲脂,也就是動態(tài)生成的代理類中只有接口中的方法,這個(gè)后面看栗子就清楚了剧包。
proxyHandler:就是InvocationHandler的實(shí)現(xiàn)類恐锦,集成管理Proxy方法的調(diào)用映射到RealSubject中,主要就是在invoke中方法實(shí)現(xiàn)疆液。在我們這個(gè)例子就是實(shí)現(xiàn)將Proxy方法調(diào)用映射到RealObject對應(yīng)的方法上一铅。
總結(jié)
動態(tài)代理和靜態(tài)代理最大的不同就是Java SDK替我們動態(tài)生成了代理類代碼,代理方法的調(diào)用最后都通過InvocationHandler映射到具體的實(shí)現(xiàn)類中堕油。動態(tài)代理在Android中應(yīng)用也是很多的潘飘,在下一篇里面會介紹一下 Android 反射注解和動態(tài)代理的綜合使用