代理模式是什么
代理模式是一種設(shè)計(jì)模式,簡(jiǎn)單說(shuō)即是在不改變?cè)创a的情況下鉴扫,實(shí)現(xiàn)對(duì)目標(biāo)對(duì)象的功能擴(kuò)展肝匆。
比如說(shuō)有一個(gè)買(mǎi)房的對(duì)象叫BuyHouser,這個(gè)對(duì)象有一個(gè)方法叫buyhouse();
public void buyhouse() {
System.out.println("我要買(mǎi)房");
}
這是一個(gè)公共的買(mǎi)房對(duì)象荷憋,你不是很喜歡,所以你希望通過(guò)自己的方式買(mǎi)房褐望。也就是對(duì)目標(biāo)對(duì)象BuyHouser的buyhouser方法進(jìn)行功能擴(kuò)展勒庄。
public void buyhouse() {
System.out.println("買(mǎi)房先準(zhǔn)備錢(qián)");
System.out.println("我買(mǎi)房了");
System.out.println("買(mǎi)完后裝修");
}
就這樣可以通過(guò)修改相關(guān)代碼實(shí)現(xiàn)上面的需求串前,但是一些情況下相關(guān)的代碼是無(wú)法修改的,這個(gè)時(shí)候我們的代理模式就閃亮登場(chǎng)了
Java中代理模式分為兩種
- 靜態(tài)代理
- 動(dòng)態(tài)代理
先說(shuō)一說(shuō)靜態(tài)代理
- 先準(zhǔn)備一個(gè)接口锅铅,靜態(tài)代理要求目標(biāo)對(duì)象和代理對(duì)象實(shí)現(xiàn)同一個(gè)接口
public interface BuyHouse {
void buyhouse();
}
- 然后準(zhǔn)備目標(biāo)對(duì)象實(shí)現(xiàn)這個(gè)接口
public class YouBuyHouse implements BuyHouse {
@Override
public void buyhouse() {
System.out.println("我要買(mǎi)房");
}
}
- 然后創(chuàng)建我們的代理對(duì)象
public class ProxyBuyHouse implements BuyHouse {
//創(chuàng)建一個(gè)對(duì)象用于接收目標(biāo)對(duì)象
private BuyHouse buyHouse;
//這里通過(guò)構(gòu)造器在代理類(lèi)實(shí)例化的時(shí)候初始化目標(biāo)對(duì)象
public ProxyBuyHouse(BuyHouse buyHouse){
this.buyHouse=buyHouse;
}
//這里是代理對(duì)象的具體方法
@Override
public void buyhouse() {
System.out.println("賣(mài)房先準(zhǔn)備錢(qián)");
//這里調(diào)用了目標(biāo)對(duì)象的方法(這里是必須的酪呻,不然就算不上代理模式了)
buyHouse.buyhouse();
System.out.println("買(mǎi)完后裝修");
}
}
代理已經(jīng)完成了,然后我們編寫(xiě)一個(gè)測(cè)試類(lèi)來(lái)看看效果
public class BuyHouseTest {
public static void main(String[] args) {
//創(chuàng)建目標(biāo)對(duì)象
BuyHouse youBuyHouse = new YouBuyHouse();
//創(chuàng)建代理對(duì)象,并傳入目標(biāo)對(duì)象
BuyHouse proxyBuyHouse = new ProxyBuyHouse(youBuyHouse);
proxyBuyHouse.buyhouse();
}
}
輸出結(jié)果是這樣的
賣(mài)房先準(zhǔn)備錢(qián)
我要買(mǎi)房
買(mǎi)完后裝修
總結(jié):其實(shí)這里做的事情無(wú)非就是盐须,創(chuàng)建一個(gè)代理類(lèi)ProxyBuyHouse玩荠,繼承了BuyHouse接口并實(shí)現(xiàn)了其中的方法。只不過(guò)這種實(shí)現(xiàn)特意包含了目標(biāo)對(duì)象的方法贼邓,正是這種特征使得看起來(lái)像是“擴(kuò)展”了目標(biāo)對(duì)象的方法阶冈。如果代理對(duì)象中只是簡(jiǎn)單地對(duì)buyhouse()方法做了另一種實(shí)現(xiàn)而沒(méi)有包含目標(biāo)對(duì)象的方法,也就不能算作代理模式了塑径。所以這里的包含是關(guān)鍵女坑。
缺點(diǎn):這種實(shí)現(xiàn)方式很直觀也很簡(jiǎn)單,但其缺點(diǎn)是代理對(duì)象必須提前寫(xiě)出统舀,如果接口層發(fā)生了變化匆骗,代理對(duì)象的代碼也要進(jìn)行維護(hù)。如果能在運(yùn)行時(shí)動(dòng)態(tài)地寫(xiě)出代理對(duì)象誉简,不但減少了一大批代理類(lèi)的代碼碉就,也少了不斷維護(hù)的煩惱,不過(guò)運(yùn)行時(shí)的效率必定受到影響闷串。這種方式就是接下來(lái)的動(dòng)態(tài)代理瓮钥。
2. 動(dòng)態(tài)代理(也叫JDK代理)
這里我們依據(jù)按照上面買(mǎi)房的例子進(jìn)行代理
public interface BuyHouse {
void buyhouse();
}
public class YouBuyHouse implements BuyHouse {
@Override
public void buyhouse() {
System.out.println("我要買(mǎi)房");
}
}
動(dòng)態(tài)代理實(shí)現(xiàn)起來(lái)也很容易
public class BuyHoseTest2 {
public static void main(String[] args) {
//創(chuàng)建目標(biāo)對(duì)象
YouBuyHouse youBuyHouse = new YouBuyHouse();
//創(chuàng)建代理對(duì)象
BuyHouse proxy = (BuyHouse) Proxy.newProxyInstance(
youBuyHouse.getClass().getClassLoader(),
youBuyHouse.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("買(mǎi)房先存錢(qián)");
Object returnValue = method.invoke(youBuyHouse,args);
System.out.println("買(mǎi)完房后裝修");
return returnValue;
}
}
);
proxy.buyhouse();
}
}
這里我們先創(chuàng)建了一個(gè)目標(biāo)對(duì)象,然后通過(guò) Proxy 類(lèi)的靜態(tài)方法newProxyInstance獲取代理類(lèi)對(duì)象
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
接收的三個(gè)參數(shù)依次為:
- ClassLoader loader:指定當(dāng)前目標(biāo)對(duì)象使用類(lèi)加載器烹吵,寫(xiě)法固定
- Class<?>[] interfaces:目標(biāo)對(duì)象實(shí)現(xiàn)的接口的類(lèi)型碉熄,寫(xiě)法固定
- InvocationHandler h:事件處理接口,需傳入一個(gè)實(shí)現(xiàn)類(lèi)(即代理類(lèi))肋拔,一般直接使用匿名內(nèi)部類(lèi)
總結(jié):這種方法因?yàn)榻?jīng)過(guò)jdk的封裝所以使用起來(lái)比較簡(jiǎn)單
缺點(diǎn):這種和靜態(tài)代理一樣需要實(shí)現(xiàn)接口才可以進(jìn)行相應(yīng)的操作锈津,如果不想這樣可以通過(guò)spring 的Cglib代理實(shí)現(xiàn)(這個(gè)下次寫(xiě))