代理模式介紹
代理模式(Proxy Pattern)也稱為委托模式柿菩。
我們生活中處處都有代理模式的影子。對于程序員來說最常接觸的就是代理上網(wǎng)了雨涛,連上代理服務(wù)器枢舶,就可以看到墻外的世界懦胞;通過中介租房,也是一種代理祟辟。
代理模式定義: 為其他對象提供一種代理以控制對這個對象的訪問医瘫。
使用場景:當(dāng)無法或不想直接訪問某個對象時可以通過一個代理對象來間接訪問。(如果租房旧困,你沒有精力去找到真正的房東醇份,所以通過中介來租房)
代理模式的組成
- 抽象角色: 通過接口或抽象類聲明真實(shí)角色實(shí)現(xiàn)的業(yè)務(wù)方法。
- 真實(shí)角色: 實(shí)現(xiàn)抽象角色吼具,定義真實(shí)角色所要實(shí)現(xiàn)的業(yè)務(wù)邏輯僚纷,供代理角色調(diào)用。
- 代理角色:實(shí)現(xiàn)抽象角色拗盒,是真實(shí)角色的代理怖竭,通過真實(shí)角色的業(yè)務(wù)邏輯方法來實(shí)現(xiàn)抽象方法,并可以附加自己的操作陡蝇。
優(yōu)點(diǎn):
- 職責(zé)清晰痊臭。真實(shí)的角色就是實(shí)現(xiàn)實(shí)際的業(yè)務(wù)邏輯,不用關(guān)心其他非本職責(zé)的事務(wù)登夫,通過后期的代理完成一件完成事務(wù)广匙,附帶的結(jié)果就是編程簡潔清晰。
- 代理對象可以在客戶端和目標(biāo)對象之間起到中介的作用恼策,這樣起到了中介的作用和保護(hù)了目標(biāo)對象的作用鸦致。
- 高擴(kuò)展性
代理模式分類
代理模式分為靜態(tài)代理和動態(tài)代理。
靜態(tài)代理是由我們自己創(chuàng)造出代理類的源碼涣楷,就像通過中介來租房分唾,我們需要先滿大街的跑找到這個中介。
動態(tài)代理則是由JDK提供的一個類來直接生成代理對象狮斗。就像同樣是租房绽乔,我們通過一個租房平臺,很容易就找到了中介碳褒。
靜態(tài)代理示例
這里我們還是以租房為示例迄汛,首先我們需要創(chuàng)建出一個接口,在接口中聲明業(yè)務(wù)方法(有房要出租)
public interface House {
// 一個普通的租房業(yè)務(wù)
public void rentingHouse();
}
然后我們再創(chuàng)建出真實(shí)角色(房東)和代理角色(中介)
// 房東
public class BeiJingOwner implements House{
@Override
public void rentingHouse() {
System.out.println("我在北京有房要出租");
}
}
// 中介
public class Proxy implements House{
private House mHouse;
public Proxy(House house) {
this.mHouse = house;
}
@Override
public void rentingHouse() {
this.mHouse.rentingHouse();
}
}
最后我們就可以去租房啦骤视,當(dāng)然我們是通過中介來租房的
public class Myself {
public static void main(String[] args) {
BeiJingOwner owner = new BeiJingOwner();
House house = new Proxy(owner);
// 這樣,我們就通過中介租到了房
house.rentingHouse();
}
}
這樣就是一個代理模式的簡單使用鹃觉,其主要還是一種委托機(jī)制专酗,真實(shí)對象將方法的執(zhí)行委托給代理對象。
當(dāng)然代理類(中介)完全可以代理多個被代理類(房主)盗扇,而具體到底是代理的哪個人祷肯,就要看代理類中所持有的實(shí)際對象類型沉填,在此示例中是BeiJingOwner,所以就是賣的北京的房子,當(dāng)然也可以代理ShanghaiHouse了佑笋。
動態(tài)代理
靜態(tài)代理如上述示例那樣翼闹,代理者的代碼由我們自己編寫。
而動態(tài)代理則與靜態(tài)代理相反蒋纬,jdk提供一個Proxy類可以直接給實(shí)現(xiàn)接口類的對象直接生成代理對象猎荠。
我們先了解一下動態(tài)代理相關(guān)的類和方法
Java.lang.reflect.Proxy類可以直接生成一個代理對象
方法名:
Proxy.newProxyInstance():
產(chǎn)生代理類的實(shí)例。僅能代理實(shí)現(xiàn)至少一個接口的類
參數(shù)類型:
ClassLoader:
類加載器蜀备。固定寫法关摇,和被代理類使用相同的類加載器即可。
Class[] interface:
代理類要實(shí)現(xiàn)的接口碾阁。固定寫法输虱,和被代理類使用相同的接口即可。
InvocationHandler:
策略(方案)設(shè)計模式的應(yīng)用.
方法名:
InvocationHandler
中的invoke
方法:調(diào)用代理類的任何方法脂凶,此方法都會執(zhí)行
參數(shù)類型:
Object proxy:
代理對象本身的引用宪睹。一般用不著。
Method method:
當(dāng)前調(diào)用的方法蚕钦。
Object[] args:
當(dāng)前方法用到的參數(shù)
相比于靜態(tài)代理亭病,我們可以不用寫代理類,其他類都保留冠桃,然后調(diào)用方法如下
public class Myself {
public static void main(String[] args) {
House owner = new BeiJingOwner();
House proxy = (House) Proxy.newProxyInstance(owner.getClass().getClassLoader(), owner.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(owner, args);
}
});
proxy.rentingHouse();
}
}
因?yàn)檎{(diào)用代理類的任何方法命贴,都會執(zhí)行InvocationHandler#invoke方法。所以我們還可以在真實(shí)類執(zhí)行之前或者之后食听,動一些手腳胸蛛。
就像通過中介去租房,中介也是會從中收取一些好處費(fèi)的一樣樱报。
現(xiàn)在葬项,我們把rentingHouse
改造成帶有參數(shù)的方法:
public interface House {
// 我們加上了一個參數(shù),就如同房租
public void rentingHouse(int price);
}
public class Myself {
public static void main(String[] args) {
House owner = new BeiJingOwner();
House proxy = (House) Proxy.newProxyInstance(owner.getClass().getClassLoader(), owner.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 我們可以在這里進(jìn)行一些操作迹蛤,就像中介收取好處費(fèi)一樣
Object[] o = new Object[]{(Integer)args[0]/2};
return method.invoke(owner, o);
}
});
proxy.rentingHouse(100);
}
}
在反射之前民珍,我們對參數(shù)進(jìn)行一些改動,就可以做一些預(yù)處理之類的操作了盗飒。當(dāng)然也能進(jìn)行一些其他操作嚷量,比如數(shù)據(jù)庫的事務(wù)開啟/關(guān)閉。