寫在之前
談到代理模式,最常用的使用場(chǎng)景莫過于 AOP 中的利用了蓝谨,在討論 AOP 的實(shí)現(xiàn)之前灌具,先來聊聊什么代理模式。
動(dòng)態(tài)代理有兩種形式譬巫,靜態(tài)代理和動(dòng)態(tài)代理咖楣,大家先不用在意兩者的概念,等了解本篇你將會(huì)發(fā)現(xiàn)其實(shí)兩者差別不大芦昔。
靜態(tài)代理
用一個(gè)簡(jiǎn)單的例子來分析什么是靜態(tài)代理诱贿,用買房張三賣房這件事兒為例,聊聊代理模式有何作用,為何如此使用如此頻繁珠十。
Subject 接口:用于對(duì)被訪問者的抽象化(比如賣房這件事兒)
SubjectImpl:被訪問者的具體實(shí)現(xiàn)(張三想要賣房)
SubjectProxy:被訪問者的代理實(shí)現(xiàn)類料扰,該類需要有一個(gè) Subject 接口具體的實(shí)例。(比如房產(chǎn)中介焙蹭,需要拿著張三授權(quán)才可以代理)
Client:訪問者的抽象晒杈。(比如李四想買房,本身是一個(gè)顧客)
代理類本身就是替被訪問者做事的孔厉,李四想買房子拯钻,提了很多要求,比如朝南撰豺、學(xué)區(qū)房粪般;房產(chǎn)中介(SubjectProxy)看張三家的房子剛好符合李四預(yù)期,將張三的房子介紹給李四污桦;相當(dāng)于當(dāng)一個(gè)中間人的意思亩歹。有人可能會(huì)說,就類似于一個(gè)介紹的活兒?jiǎn)峁讶螅糠堑米屩薪閬韱崂υ鳎咳绻皇呛?jiǎn)單的介紹舅柜,還真不需要中介梭纹,但是中介可以幫忙跑貸款、幫忙把握合同等致份。
這樣变抽,張三只需要授權(quán)給中介,就可以一邊做別的事兒氮块,一邊更加省心地完成交易绍载。
上面的圖順理成章變成了如下的模式
被代理的抽象接口——房子
public interface House {
/**
* 賣房子
*/
void sell();
}
被訪問的類——張三的房子
public class HouseForZhangsan implements House {
@Override
public void sell() {
System.out.println("zhangsan sell house…………");
}
}
代理類——房產(chǎn)中介
public class HouseProxy implements House {
private House house;
// 通過構(gòu)造方法做到每次替不同的人代理
public HouseProxy (House house) {
this.house = house;
}
@Override
public void sell() {
house = new HouseForZhangsan();
house.sell();
}
}
測(cè)試類——交易場(chǎng)所
public class Test {
public static void main(String[] args) {
// 構(gòu)造具體的賣房者
House house = new HouseForZhangsan();
// 將張三交給中介代理
House houseForPerson = new HouseProxy(house);
houseForPerson.sell();
}
}
還是回到 Spring AOP 模式中,其中 SubjectProxy 就像是 SubjectImpl 的中介滔蝉,而 SubjectImpl 本身是系統(tǒng)中的 JoinPoint 所在的對(duì)象(目標(biāo)對(duì)象)击儡,順理成章地為目標(biāo)對(duì)象創(chuàng)建一個(gè)代理對(duì)象,完成切面的邏輯蝠引。
但是各位想想阳谍,市面上并不是只有房子賣呢,張三家里有一輛空閑的車螃概,也想賣矫夯,還能去找房產(chǎn)中介嗎?
肯定不能了吊洼。
于是催生出了專門用于車交易的中介训貌,比如瓜子二手車(號(hào)稱沒有中間商賺差價(jià),哈哈哈)、二手車之家等等递沪。
<img src="https://cdn.jsdelivr.net/gh/kaoyan-guide/personPic@main/imgBlog/spring/20210417205209.png" alt="二手車平臺(tái)" style="zoom:50%;" />
再比如萬一張三突然發(fā)現(xiàn)手機(jī)用久了豺鼻,想賣掉二手手機(jī),新的 iPhone款慨,于是又出現(xiàn)了各種各樣的二手手機(jī)平臺(tái)(其實(shí)本質(zhì)也是一種中介)
<img src="https://cdn.jsdelivr.net/gh/kaoyan-guide/personPic@main/imgBlog/spring/20210417205454.png" alt="二手手機(jī)" style="zoom:40%;" />
……
……
等等拘领,那有人可能會(huì)想,能不能搞一個(gè)中介樱调,能夠啥都賣呢约素?于是更加全面的二手平臺(tái)應(yīng)運(yùn)而生了。
在這上面可以靈活的代理各種商品 (被代理對(duì)象)笆凌,這就達(dá)到了一種動(dòng)態(tài)中介的效果圣猎。
對(duì),沒錯(cuò)乞而,動(dòng)態(tài)代理已經(jīng)介紹完了送悔。
<img src="https://cdn.jsdelivr.net/gh/kaoyan-guide/personPic@main/imgBlog/spring/20210417210047.png" alt="image-20210417210040941" style="zoom:50%;" />
開玩笑,繼續(xù)聊聊 Spring AOP 是如何利用動(dòng)態(tài)代理的爪模。
動(dòng)態(tài)代理
可以指定接口在運(yùn)行期間動(dòng)態(tài)的生成代理對(duì)象欠啤。(換句話說:無論你要賣什么,你來的時(shí)候都可以給你找一個(gè)對(duì)應(yīng)的中介)
那么如何動(dòng)態(tài)生成代理類呢屋灌?
需要借助兩個(gè)工具洁段,一個(gè)是 java.lang.reflect.Proxy
類 和 java.lang.reflect.InvocationHandler
,問題的關(guān)鍵在于如何實(shí)時(shí)的給客戶產(chǎn)生一個(gè)滿足要去的中介共郭。
這個(gè)就是借助 InvocationHandler
來動(dòng)態(tài)生成代理類祠丝,還是以上面中介為例,我們姑且講要生成的代理類叫做 target
.
如何動(dòng)態(tài)產(chǎn)生不同類型的中介除嘹?
第一步肯定需要知道此時(shí)替什么類型客戶代理写半,但是又不能寫得太死,我們姑且在生成代理類中先聲明一個(gè) 被代理的對(duì)象尉咕。
第二步:通過某種方式將 被代理對(duì)象通過傳入的方式傳進(jìn)來
第三步:將被代理對(duì)象與中介進(jìn)行綁定叠蝇。
/**
* 被代理的目標(biāo)
*/
public Object target;
/**
* 綁定委托對(duì)象,并且生成代理類
* @param target
* @return
*/
public Object bind(Object target) {
this.target = target;
//綁定該類實(shí)現(xiàn)的所有接口年缎,取得代理類
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
上述幾步部署完成之后悔捶,會(huì)明白中介要替什么人做事兒,中介做什么事兒晦款,并且將中介與客戶關(guān)聯(lián)起來炎功。
最后才是真正的替客戶做事兒。
public class SellInvocationHandler implements InvocationHandler {
/**
* 被代理的目標(biāo)
*/
public Object target;
/**
* 綁定委托對(duì)象缓溅,并且生成代理類
* @param target
* @return
*/
public Object bind(Object target) {
this.target = target;
//綁定該類實(shí)現(xiàn)的所有接口蛇损,取得代理類
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log.info("額外邏輯……");
return method.invoke(target, args);
}
}
還記得我們之前說過的嗎?
動(dòng)態(tài)代理解決的只是靈活產(chǎn)生不同代理類(換句話說靈活搭配不同類型中介)
至于做什么類型事兒,和替什么人做什么事兒這兩件事兒還是得存在淤齐。
因此仍然需要申明兩個(gè)類股囊。
做什么類型事兒
public interface House {
/**
* 賣房子
*/
void sell();
}
替什么人做什么事兒
public class HouseForZhangsan implements House {
@Override
public void sell() {
System.out.println("zhangsan sell house…………");
}
}
然后就可以愉快地進(jìn)行交易了,每次有新的顧客來更啄,就可以叫不同類型的中介來服務(wù)稚疹。
public class DynamicProxyTest {
public static void main(String[] args) {
SellInvocationHandler invocationHandler = new SellInvocationHandler();
// 將被訪問類和代理類相互綁定( 將房產(chǎn)中介 與 房子賣者相互綁定 )
House house = (House) invocationHandler.bind(new HouseForZhangsan());
// 真正執(zhí)行
house.sell();
}
}
至此,我們已經(jīng)完成了真正的靈活代理工作祭务。
動(dòng)態(tài)代理雖好内狗,卻不能解決所有的事情。比如义锥,動(dòng)態(tài)代理只能對(duì)實(shí)現(xiàn)了相應(yīng)接口 (Interface) 的類使用柳沙,如果某個(gè)類沒有實(shí)現(xiàn)任何的 Interface,就無法使用動(dòng)態(tài)代理機(jī)制為其生成相應(yīng)的動(dòng)態(tài)代理對(duì)象拌倍。