傀儡政權(quán)之代理模式

點(diǎn)擊上方「藍(lán)字」關(guān)注我們

代理模式應(yīng)用非常廣泛,特別java領(lǐng)域的Spring框架,可以說把代理模式運(yùn)用到極致。其中Spring的代理又分JDK動態(tài)代理和cglib動態(tài)代理。這類不打算深入講解Spring的動態(tài)代理,而是深入講解一下GOF 23的代理模式。
0x01:代理模式
代理模式:給某一個(gè)對象提供一個(gè)代理對象,并由代理對象控制對原對象的引用。通俗的來講代理模式就是生活中常見的中介。好比房地產(chǎn)中介,買賣房子時(shí)通過中介,既安全,又方便,唯一的不足就是需要交納一筆不菲的傭金。代理模式的通用UML類圖如下

抽象角色(Subject):通過接口或抽象類聲明真實(shí)角色實(shí)現(xiàn)的業(yè)務(wù)方法。
真實(shí)角色(RealSubject):實(shí)現(xiàn)抽象角色,定義真實(shí)角色所要實(shí)現(xiàn)的業(yè)務(wù)邏輯,供代理角色調(diào)用。
代理角色(Proxy):實(shí)現(xiàn)抽象角色,是真實(shí)角色的代理,通過真實(shí)角色的業(yè)務(wù)邏輯方法來實(shí)現(xiàn)抽象方法,并可以附加額外的操作來增強(qiáng)功能和業(yè)務(wù)能力。
抽象角色(Subject):
public?interface?Subject?{
????public?void?request();
?}
真實(shí)角色(RealSubject):
public?class?RealSubject?implements?Subject?{
????@Override
????public?void?request()?{
????????System.out.println("真實(shí)的請求RealSubject");
????}
}
代理角色(Proxy):
public?class?Proxy?implements?Subject?{
????private?RealSubject?realSubject?=?null;
????public?Proxy()?{
????????this.realSubject?=?new?RealSubject();
????}
????@Override
????public?void?request()?{
????????this.doBefore();
????????this.realSubject.request();
????????this.doAfter();
????}
????//前置處理
????private?void?doBefore()?{
????????System.out.println("-------do?before------");
????}
????//后綴處理
????private?void?doAfter()?{
????????System.out.println("-------do?after-------");
????}
}
Client客戶端:
public?class?Client?{
????public?static?void?main(String[]?args)?{
????????Proxy?proxy?=?new?Proxy();
????????proxy.request();
????}
}
通過以上的代碼就實(shí)現(xiàn)了一個(gè)簡單的代理模式,這種實(shí)現(xiàn)方式也叫靜態(tài)代理;但是實(shí)在項(xiàng)目、框架中的代理模式可并沒有這么簡單,比這復(fù)雜多了。
靜態(tài)代理的特點(diǎn):
靜態(tài)代理由開發(fā)者去生成固定的代碼進(jìn)行編譯。需要定義接口或抽象的父類作為抽象目標(biāo)類,具體目標(biāo)類和代理類一起實(shí)現(xiàn)相同的接口或繼承相同的類,然后通過調(diào)用相同的方法來調(diào)用目標(biāo)對象的方法。
靜態(tài)代理需要目標(biāo)對象和代理對象實(shí)現(xiàn)相同的接口。可以在不修改目標(biāo)對象功能的前提下,對目標(biāo)功能進(jìn)行擴(kuò)展和增強(qiáng)。
雖然靜態(tài)代理可以很好的對目標(biāo)對象進(jìn)行功能擴(kuò)展,但是每一個(gè)真實(shí)角色都需要建立代理類,工作量較大且不易管理;而且如果接口發(fā)生改變的話,代理類也必須進(jìn)行相應(yīng)的修改,這時(shí)動態(tài)代理的作用就顯現(xiàn)出來了。
0x02:動態(tài)代理
動態(tài)代理與靜態(tài)代理的區(qū)別在于:動態(tài)代理類的字節(jié)碼是在程序運(yùn)行時(shí)由Java反射機(jī)制動態(tài)生成,無需程序員手工編寫它的源代碼。動態(tài)代理分為JDK動態(tài)代理和cglib動態(tài)代理。
JDK動態(tài)代理是jre提供的類庫,只需依賴JDK,就可以直接使用,無需依賴第三方類庫。
最近蛋殼真是火的一塌糊涂,先來個(gè)抽象房東接口
public?interface?IFangDong?{
????public?void?hire(String?area);
}
房東實(shí)現(xiàn)接口
public?class?FangDong?implements?IFangDong{
????@Override
????public?void?hire(String?area)?{
????????System.out.println(area);
????}
}
房東一般指那些手握幾套甚至幾十臺房子的人,房東有房子出租,但是房子太多管不過來,一般不會自己聯(lián)系要租房子的人;而是交個(gè)房地產(chǎn)中介。創(chuàng)建一個(gè)類,便于理解該類的類名以Prox結(jié)尾,但是該類不是代理類,因?yàn)樗]有實(shí)現(xiàn)IFangDong接口,無法對外提供服務(wù),僅僅是一個(gè)wrapper類(包裝類)。
import?java.lang.reflect.InvocationHandler;
import?java.lang.reflect.Method;
import?java.lang.reflect.Proxy;
public?class?FangDongProxy?implements?InvocationHandler{
????//?目標(biāo)類,也就是被代理對象
????private?Object?target;
????public?void?setTarget(Object?target)
????{
????????this.target?=?target;
????}
????@Override
????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable
????{
????????//?這里可以做增強(qiáng)
????????System.out.println("必須租給程序員~~因?yàn)樗麄內(nèi)松靛X多");
????????Object?result?=?method.invoke(target,?args);
????????return?result;
????}
????//?生成代理類
????public?Object?creatProxyedObj()
????{
????????return?Proxy.newProxyInstance(target.getClass().getClassLoader(),?target.getClass().getInterfaces(),?this);
????}?
}
方法creatProxyedObj返回的對象才是代理類,它需要三個(gè)參數(shù),前兩個(gè)參數(shù)的意思是在同一個(gè)classloader下通過接口創(chuàng)建出一個(gè)對象,該對象需要一個(gè)屬性,也就是第三個(gè)參數(shù),它是一個(gè)InvocationHandler對象。上述代理的代碼使用過程一般如下:
new一個(gè)目標(biāo)對象
new一個(gè)InvocationHandler,將目標(biāo)對象set進(jìn)去
通過creatProxyedObj創(chuàng)建代理對象,強(qiáng)轉(zhuǎn)為目標(biāo)對象的接口類型即可使用,實(shí)際上生成的代理對象實(shí)現(xiàn)了目標(biāo)接口。
public?class?Client?{
????public?static?void?main(String[]?args)?{
????????IFangDong?fd?=?new?FangDong();
????????FangDongProxy?proxy?=?new?FangDongProxy();
????????proxy.setTarget(fd);?
????????Object?obj?=?proxy.creatProxyedObj();
????????IFangDong?fangDong?=?(IFangDong)obj;
????????fangDong.hire("我有130平大房子出租~~");
????}
}
從以上代碼可以看出如果使用JDK動態(tài)代理,必須實(shí)現(xiàn)InvocationHandler類,然后再該類的invoke方法做增強(qiáng)和調(diào)用目標(biāo)類的相應(yīng)方法。
cglib動態(tài)代理,需要引入第三方類庫cglib-x.x.x.jar
通過繼承可以繼承父類所有的公開方法,然后重寫這些方法;在重寫時(shí)對方法進(jìn)行增強(qiáng),這就是cglib的核心思想。根據(jù)里氏代換原則(LSP),父類出現(xiàn)的地方,子類都可以出現(xiàn),所以cglib實(shí)現(xiàn)的代理就一定可以被正常使用。
引入jar包
<dependency>
????<groupId>cglibgroupId>
????<artifactId>cglibartifactId>
????<version>3.1version>
dependency>
import?java.lang.reflect.Method;
import?net.sf.cglib.proxy.Enhancer;
import?net.sf.cglib.proxy.MethodInterceptor;
import?net.sf.cglib.proxy.MethodProxy;
public?class?FangDongCglibProxy?implements?MethodInterceptor?{
????//?根據(jù)一個(gè)類型產(chǎn)生代理類,此方法不要求一定放在MethodInterceptor中
????public?Object?CreatProxyedObj(Class>?clazz)?{
????????Enhancer?enhancer?=?new?Enhancer();
????????enhancer.setSuperclass(clazz);
????????enhancer.setCallback(this);
????????return?enhancer.create();
????}
????@Override
????public?Object?intercept(Object?obj,?Method?method,?Object[]?args,?MethodProxy?methodProxy)?throws?Throwable?{
????????//?這里增強(qiáng)
????????System.out.println("FangDongCglibProxy》》》必須租給程序員~~因?yàn)樗麄內(nèi)松靛X多");
????????return?methodProxy.invokeSuper(obj,?args);
????}
}
public?class?Client?{
????public?static?void?main(String[]?args)?{
????????FangDongCglibProxy?fangDongCglibProxy?=?new?FangDongCglibProxy();
????????FangDong?fangDong?=?(FangDong)fangDongCglibProxy.creatProxyedObj(FangDong.class);
????????fangDong.hire("我有130平大房子出租~~");
????}
}