現(xiàn)實生活中疯淫,存在著各種代理地来,比如海外代購,律師熙掺,游戲代練等等未斑,映射到Java中,也存在著代理币绩,匯總實際場景中的使用蜡秽,將代理分為了靜態(tài)代理和動態(tài)代理兩種模式。
StaticProxy
靜態(tài)代理拆開來缆镣,靜態(tài)指的是在程序運行前就已經(jīng)存在了代理類的字節(jié)碼文件芽突,也就是這個代理類是程序員手動編寫的,代理類和被代理類的關(guān)系就已經(jīng)確定了的董瞻,代理指的就是編寫的代理類對被代理類的增強效果寞蚌。
場景一:
張三看上了海外一家公司CompanyA制造的一款包田巴,如果他自己想要買這款包,他就需要出國購買挟秤,出國前辦理簽證壹哺,坐飛機,買包艘刚,坐飛機管宵,回家。但他并不像這么麻煩攀甚,于是他便找了一家代購店箩朴,于是張三所做的事情就是給錢,拿包完事云稚,至于中間代購店如何出的國隧饼,如何坐的飛機,跟張三毫無關(guān)系静陈,因為張三的目的就是要這個包燕雁,抽象如下:
業(yè)務(wù)接口BusinessA(制造包)
/**
* Description: 制造包的業(yè)務(wù)接口
*
* @Author 70KG
* @Date 2019/3/12
* @Since v1.0
*/
public interface BusinessA {
void createBag();
}
生產(chǎn)包的廠商CompanyA:
/**
* Description: 國外生產(chǎn)包的廠商A,但不限于A鲸拥,可能還有B拐格,C 。刑赶。捏浊。
*
* @Author 70KG
* @Date 2019/3/12
* @Since v1.0
*/
public class CompanyA implements BusinessA {
@Override
public void createBag() {
System.out.println("CompanyA公司生產(chǎn)的包。");
}
}
代理類ProxyStore:
/**
* Description: 代理商店撞叨,它對外宣稱有賣包的業(yè)務(wù)
*
* @Author 70KG
* @Date 2019/3/12
* @Since v1.0
*/
public class ProxyStore implements BusinessA {
private CompanyA companyA;
@Override
public void createBag() {
// 1. 在購買前做的事
doBefore();
// 2. 從廠商購買
companyA.createBag();
// 3. 運回國內(nèi)
doAfter();
}
private void doBefore() {
System.out.println("辦理簽證金踪,坐飛機去國外。");
}
private void doAfter() {
System.out.println("從國外運回國內(nèi)牵敷。");
}
public CompanyA getCompanyA() {
return companyA;
}
public void setCompanyA(CompanyA companyA) {
this.companyA = companyA;
}
}
測試:
/**
* Description:
*
* @Author 70KG
* @Date 2019/3/12
* @Since v1.0
*/
public class Test01 {
@Test
public void m1() {
// 創(chuàng)建代理類
ProxyStore proxyStore = new ProxyStore();
// 讓代理有買包的權(quán)利
proxyStore.setCompanyA(new CompanyA());
// 由代理去購買
proxyStore.createBag();
}
}
靜態(tài)代理的優(yōu)缺點:
- 優(yōu)點:業(yè)務(wù)類只需要關(guān)注業(yè)務(wù)本身胡岔,無需關(guān)心業(yè)務(wù)前后所做的增強處理,這些都交給代理來處理枷餐。
- 缺點:代理類代理的業(yè)務(wù)單一靶瘸,假如業(yè)務(wù)接口擴展了,還需要改動代理類的代碼毛肋,增加維護的成本怨咪。
DynamicProxy
動態(tài)代理拆開,動態(tài)指的是代理類的源碼是在程序運行期間由JVM根據(jù)反射等機制動態(tài)的生成润匙,所以不存在代理類的字節(jié)碼文件诗眨。代理類和委托類的關(guān)系是在程序運行時確定。
- Proxy類孕讳,它是所有動態(tài)代理的父類辽话,其中利用靜態(tài)方法newProxyInstance()來創(chuàng)建代理類肄鸽。
- InvocationHandler接口,其中invoke方法的作用就是在代理類中調(diào)用目標方法油啤,并返回結(jié)果典徘。
- invoke方法的三個參數(shù)
- proxy:代理對象的實例
- method:通過它可以調(diào)用真實方法
- args:傳過來的參數(shù)
還是上面靜態(tài)代理的例子(以下為JDK動態(tài)代理,只能代理接口):
業(yè)務(wù)接口BusinessA:
/**
* Description: 制造包的業(yè)務(wù)接口
*
* @Author 70KG
* @Date 2019/3/12
* @Since v1.0
*/
public interface BusinessA {
void createBag();
}
業(yè)務(wù)接口BusinessB:
/**
* Description: 制造手表的業(yè)務(wù)接口
*
* @Author 70KG
* @Date 2019/3/12
* @Since v1.0
*/
public interface BusinessB {
void createWatch();
}
生產(chǎn)包的廠商CompanyA:
/**
* Description: 國外生產(chǎn)包的廠商A益咬,但不限于A逮诲,可能還有B,C 幽告。梅鹦。。
*
* @Author 70KG
* @Date 2019/3/12
* @Since v1.0
*/
public class CompanyA implements BusinessA {
@Override
public void createBag() {
System.out.println("CompanyA公司生產(chǎn)的包冗锁。");
}
}
生產(chǎn)手表的廠商CompanyB:
/**
* Description: 國外生產(chǎn)手表的廠商CompanyB
*
* @Author 70KG
* @Date 2019/3/12
* @Since v1.0
*/
public class CompanyB implements BusinessB {
@Override
public void createWatch() {
System.out.println("CompanyB公司生產(chǎn)的手表齐唆。");
}
}
代理類:
需要實現(xiàn)InvocationHandler接口,并且重寫invoke方法冻河,還需要一個獲取代理對象的方法getProxyInstance箍邮。
/**
* description 實現(xiàn)InvocationHandler接口的動態(tài)代理類
*
* @author 70KG
* @date 2019/3/12
*/
public class ProxyClassBeans implements InvocationHandler {
/**
* 被代理的對象
**/
private Object tar;
/**
* 利用反射來調(diào)用目標方法
**/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1. 調(diào)用目標方法之前
doBefore();
// 2. 調(diào)用目標方法
Object invoke = method.invoke(tar, args);
// 3. 調(diào)用目標方法之后
doAfter();
return invoke;
}
/** 綁定該類實現(xiàn)的所有接口,取得代理類 **/
public Object getProxyInstance() {
return Proxy.newProxyInstance(tar.getClass().getClassLoader(), tar.getClass().getInterfaces(), this);
}
// ===================動態(tài)代理類中的增強方法========================
public void doBefore() {
System.out.println("在國外尋找優(yōu)質(zhì)的貨源叨叙。");
}
public void doAfter() {
System.out.println("精包裝安全郵寄回國內(nèi)锭弊。");
}
// ===========================get/set=============================
public Object getTar() {
return tar;
}
public void setTar(Object tar) {
this.tar = tar;
}
}
測試:
/**
* description
*
* @author 70KG
* @date 2019/3/12
*/
public class MainTest {
public static void main(String[] args) {
// 獲取動態(tài)代理類
ProxyClassBeans proxyClassBeans = new ProxyClassBeans();
IBusinessA companyA = new CompanyA();
// 將目標類交給代理類
proxyClassBeans.setTar(companyA);
// 拿到代理對象
IBusinessA instance1 = (IBusinessA) proxyClassBeans.getProxyInstance();
instance1.createBag();
System.out.println("=================================");
IBusinessB companyB = new CompanyB();
proxyClassBeans.setTar(companyB);
IBusinessB instance2 = (IBusinessB) proxyClassBeans.getProxyInstance();
instance2.saleWatch();
}
}
運行結(jié)果:
在國外尋找優(yōu)質(zhì)的貨源。
CompanyA公司生產(chǎn)的包擂错。
精包裝安全郵寄回國內(nèi)味滞。
=================================
在國外尋找優(yōu)質(zhì)的貨源。
CompanyB公司生產(chǎn)的手表钮呀。
精包裝安全郵寄回國內(nèi)剑鞍。
由運行結(jié)果可知,動態(tài)代理在增添業(yè)務(wù)的時候(增添了賣手表的業(yè)務(wù))爽醋,只需要調(diào)用者傳入不同的接口就好了攒暇,代理類不需要改動。
動態(tài)代理的優(yōu)缺點:
- 優(yōu)點:同樣子房,我們只關(guān)注業(yè)務(wù)類的邏輯即可,并且如果新增業(yè)務(wù)類就轧,代理類的代碼根本不需要動证杭,因為代理對象是根據(jù)傳入的接口類型生成的,方便擴展妒御。
- 缺點:JDK動態(tài)代理解愤,在生成代理對象的時候必須傳入業(yè)務(wù)類的接口類型,也就是只能夠代理接口乎莉。能夠代理類的動態(tài)代理是CGLIB動態(tài)代理送讲。