靜態(tài)代理
基本概念
目標(biāo)對(duì)象為其對(duì)象提供一種代理對(duì)象,其他對(duì)象通過代理對(duì)象來控制對(duì)目標(biāo)對(duì)象的訪問完残。
角色劃分
Proxy(美['prɑksi]):代理對(duì)象,其他對(duì)象直接控制的對(duì)象侮邀。
Subject([?s?bd??kt]):目標(biāo)接口绊茧,目標(biāo)對(duì)象的抽象。
RealSubject:具體目標(biāo)對(duì)象毅哗,目標(biāo)接口的實(shí)現(xiàn),即真正控制的對(duì)象仑乌。
簡(jiǎn)單理解:
當(dāng)其他對(duì)象對(duì)某個(gè)目標(biāo)對(duì)象進(jìn)行操作且需要對(duì)目標(biāo)對(duì)象進(jìn)行更改時(shí)晰甚,若目標(biāo)對(duì)象被多個(gè)對(duì)象引用厕九,或目標(biāo)對(duì)象不可以更改扁远,此時(shí)使用代理模式,通過對(duì)代理對(duì)象的修改并闲,而完成最終的業(yè)務(wù)需求焙蚓。
來源于生活的案例
- 案例描述: 我通過代購(gòu)洒宝,買了一臺(tái)iphone X雁歌。
我想要買一臺(tái)港版的iphone X靠瞎,但是我在大陸,不能直接購(gòu)買港版的iphone X佳窑。于是神凑,我找到了代購(gòu)公司何吝,代購(gòu)公司幫我買一個(gè)iphoneX爱榕。
目標(biāo)接口:買手機(jī)動(dòng)作,行為抽象
目標(biāo)對(duì)象:我->買手機(jī)的人->買手機(jī)的行為
代理對(duì)象:代購(gòu)的人->同學(xué)或者朋友->代理買手機(jī)的行為
其他對(duì)象:調(diào)用購(gòu)買手機(jī)的對(duì)象
-
案例理解: 我為什么是目標(biāo)對(duì)象藻三?
在考慮整個(gè)案例之后棵帽,大多數(shù)人一定會(huì)主觀認(rèn)為目標(biāo)對(duì)象應(yīng)該是手機(jī)岖寞,但是將每個(gè)對(duì)象進(jìn)行行為劃分仗谆,即可理解為目標(biāo)對(duì)象是我,即我買手機(jī)的這個(gè)行為隶垮。
整個(gè)案例的核心勉耀,是我想要買手機(jī)便斥,代購(gòu)的核心是幫我買手機(jī)威始,而手機(jī)卻是具有其他行為的黎棠,與買手機(jī)行為無關(guān)的對(duì)象脓斩。
所以木西,以買手機(jī)的行為方向,進(jìn)行思考随静,可以得出以下結(jié)論:
目標(biāo)接口:買手機(jī)動(dòng)作八千,行為抽象
目標(biāo)對(duì)象:我->買手機(jī)的人->買手機(jī)的行為
代理對(duì)象:代購(gòu)的人->同學(xué)或者朋友->代理買手機(jī)的行為
其他對(duì)象:調(diào)用購(gòu)買手機(jī)的對(duì)象
- 案例代碼實(shí)現(xiàn): 代碼實(shí)現(xiàn)三步走;
1.將行為抽象挪挤,實(shí)現(xiàn)目標(biāo)接口
目標(biāo)接口:IShopPhone(購(gòu)物->手機(jī))
2.實(shí)現(xiàn)行為叼丑,完成購(gòu)買動(dòng)作
目標(biāo)對(duì)象:ShopPhone
特點(diǎn):實(shí)現(xiàn)目標(biāo)接口
3.實(shí)現(xiàn)代理對(duì)象,改變由代理對(duì)象完成扛门。
代理對(duì)象:WJProxy
特點(diǎn)一:實(shí)現(xiàn)目標(biāo)接口(可有可無)
特點(diǎn)二:持有目標(biāo)對(duì)象的引用(必需)
源碼展示
/**
* 目標(biāo)接口,抽象購(gòu)買行為
*/
public interface IShopPhone {
/**
* 購(gòu)買手機(jī)
* @param str
*/
void shopPhone(String str);
}
/**
* 目標(biāo)接口的實(shí)現(xiàn)類纵寝,實(shí)現(xiàn)具體的行為
*/
public class ShopPhone implements IShopPhone {
@Override
public void shopPhone(String str) {
System.out.println(str);
}
}
/**
* 代理類论寨,代理購(gòu)買手機(jī)的行為。
*/
public class ProxyShopPhone implements IShopPhone {
IShopPhone mTarget;
/**
* 目標(biāo)
*/
public ProxyShopPhone(IShopPhone target) {
this.mTarget = target;
}
@Override
public void shopPhone(String str) {
System.out.println("-----start----");
mTarget.shopPhone(str);
System.out.println("-----end----");
}
}
/**
* 用戶類爽茴,用戶進(jìn)行買手機(jī)的操作
*/
public class User {
public static void main(String[] args) {
/*在大陸買手機(jī)*/
ShopPhone shopPhone = new ShopPhone();
shopPhone.shopPhone("在大陸買了一個(gè)iPhone X火焰,一共花了8888元");
/*在香港買手機(jī)*/
ProxyShopPhone proxyShopPhone = new ProxyShopPhone(shopPhone);
proxyShopPhone.shopPhone("通過代購(gòu),在香港買了一個(gè)iphone X谦疾,一共花了6666元");
}
}
————————————————————控制臺(tái)顯示的結(jié)果—————————————————————
在大陸買了一個(gè)iPhone X晚顷,一共花了8888元
-----start----
通過代購(gòu)词爬,在香港買了一個(gè)iphone X恋沃,一共花了6666元
-----end----
Process finished with exit code 0
——————————————END——————————————
動(dòng)態(tài)代理
與靜態(tài)代理的區(qū)別
動(dòng)態(tài)創(chuàng)建代理類(虛擬機(jī)->框架塔橡、系統(tǒng)幫助我們來完成創(chuàng)建過程)
動(dòng)態(tài)代理的特點(diǎn)
1.代理對(duì)象不需要實(shí)現(xiàn)接口。
2.不需要自己實(shí)現(xiàn)代理對(duì)象,由虛擬機(jī)動(dòng)態(tài)生成(內(nèi)部通過java反射實(shí)現(xiàn))双仍。
3.動(dòng)態(tài)代理也叫做JDK代理或接口代理逗物。
代碼實(shí)現(xiàn)
將動(dòng)態(tài)代理中秀睛,不使用靜態(tài)代理中的代理對(duì)象田盈,通過jdk中自帶的代理方法實(shí)現(xiàn)動(dòng)態(tài)代理。
/**
* 操作類---測(cè)試類
*/
public class Main {
public static void main(String[] args) {
/*在大陸買手機(jī)*/
ShopPhone shopPhone = new ShopPhone();
shopPhone.shopPhone("在大陸買了一個(gè)iPhone X,一共花了8888元");
/*動(dòng)態(tài)代理---在香港買手機(jī)*/
IShopPhone proxy = (IShopPhone) Proxy.newProxyInstance(shopPhone.getClass().getClassLoader(), shopPhone.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("-----動(dòng)態(tài)代理 start----");
Object returnValue = method.invoke(shopPhone, args);
System.out.println("-----動(dòng)態(tài)代理 end----");
return returnValue;
}
});
proxy.shopPhone("通過代購(gòu)肛跌,在香港買了一個(gè)iphone X,一共花了6666元");
}
- newProxyInstance詳解:
ClassLoader loader: 指定當(dāng)前目標(biāo)對(duì)象使用類加載器,即目標(biāo)對(duì)象的類加載器眷柔。獲取方法是固定的:[shopPhone.getClass().getClassLoader()]喳坠。
Class<?>[] interfaces: 目標(biāo)對(duì)象實(shí)現(xiàn)的接口的類型,使用泛型方式確認(rèn)類型聋涨。獲取方法是固定的:[shopPhone.getClass().getInterfaces()]茂腥。
InvocationHandler h: 事件處理,執(zhí)行目標(biāo)對(duì)象的方法時(shí),會(huì)觸發(fā)事件處理器的方法,會(huì)把當(dāng)前執(zhí)行目標(biāo)對(duì)象的方法作為參數(shù)傳入般渡。即動(dòng)態(tài)代理類接口的實(shí)現(xiàn)蝴乔,通過JAVA反射實(shí)現(xiàn)。
封裝動(dòng)態(tài)代理工廠代碼實(shí)現(xiàn)
/**
* 動(dòng)態(tài)代理工廠
*/
public class ProxyFactory {
private Object mTarget;
/**
* 維護(hù)一個(gè)目標(biāo)對(duì)象
*/
public ProxyFactory(Object target) {
this.mTarget = target;
}
/**
* 獲取動(dòng)態(tài)代理對(duì)象
*
* @return
*/
public Object getProxyInstance() {
return Proxy.newProxyInstance(mTarget.getClass().getClassLoader(), mTarget.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("--------start-------");
Object returnValue = method.invoke(mTarget,args);
System.out.println("---------end--------");
return returnValue;
}
});
}
}
——————————————————————使用方法—————————————————————
proxy.shopPhone("通過代購(gòu),在香港買了一個(gè)iphone X,一共花了6666元");
IShopPhone proxy2 = (IShopPhone) new ProxyFactory(shopPhone).getProxyInstance();
proxy2.shopPhone("通過代購(gòu)商店霞捡,在香港買了一個(gè)iphone X街夭,一共花了6666元");
——————————————————————控制臺(tái)輸出結(jié)果—————————————————————
在大陸買了一個(gè)iPhone X,一共花了8888元
--------start-------
通過代購(gòu)商店埃碱,在香港買了一個(gè)iphone X乃正,一共花了6666元
---------end--------
Process finished with exit code 0
cglib動(dòng)態(tài)代理
基本概述
可繼承式的動(dòng)態(tài)代理(java僅允許單繼承凡人,而JDK中的代理類挠轴,自身就會(huì)繼承Proxy)启上,在android開發(fā)中包券,一般不會(huì)使用付秕。
使用該方式實(shí)現(xiàn)動(dòng)態(tài)代理嵌削,需要倒入cglib.jar艇劫,或使用spring框架吼驶,即該方式的動(dòng)態(tài)代理,大多在后臺(tái)服務(wù)器中使用店煞,在本片文章中不會(huì)過多講解蟹演;
代碼實(shí)現(xiàn)
/**
/**
* 通過繼承實(shí)現(xiàn)的動(dòng)態(tài)代理類
*/
public class CGLibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class<?> clazz){
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
/**
* 攔截所有目標(biāo)類方法的調(diào)用
* 參數(shù):
* obj目標(biāo)實(shí)例對(duì)象
*method 目標(biāo)方法的反射對(duì)象
* args方法的參數(shù)
* proxy代理類的實(shí)例
*/
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
//代理類調(diào)用父類的方法
System.out.println("日志開始");
proxy.invokeSuper(obj, args);
System.out.println("日志結(jié)束");
return null;
}
}
——————————————————————使用方法—————————————————————
/*通過cglib.jar實(shí)現(xiàn)可繼承的動(dòng)態(tài)代理*/
IShopPhone proxy3 = (IShopPhone) new CGLibProxy().getProxy(ShopPhone.class);
proxy3.shopPhone("通過代購(gòu),在香港買了一個(gè)iphone X顷蟀,一共花了6666元");
使用場(chǎng)景
例如:開發(fā)當(dāng)中->類似框架(代理模式)
XUtils框架酒请、Retrofit框架、MVP架構(gòu)設(shè)計(jì)鸣个、插件化架構(gòu)設(shè)計(jì)等等...
最后羞反,為自己留一個(gè)作業(yè):如何通過java反射自己實(shí)現(xiàn)JDK動(dòng)態(tài)代理?