說到動態(tài)代理模式,大家一定會想到spring的AOP概念,今天我們來說一下java的動態(tài)代理模式
概念:為另一個對象提供一個替身患者占位符以控制對這個對象的訪問。
代理模式有好多種變種,常見的分別為:遠程代理、虛擬代理、保護代理、緩存代理。
簡單介紹一下這三種代理的使用場景:
1.遠程代理:最典型的例子就是Java的RMI,客戶端通過代理想要調(diào)用遠程服務端的方法實現(xiàn)。此時代理要做的就是將客戶端想要調(diào)用的方法通過代理使用網(wǎng)絡傳輸將數(shù)據(jù)序列化、反序列化調(diào)用另一臺服務器的服務方法。
2.虛擬代理:直到我們真正需要對象的時候才創(chuàng)建它,當對象再創(chuàng)建前創(chuàng)建中時,由虛擬代理來扮演。對象的替身,對象創(chuàng)建后代理會將請求直接委托給對象。
3.保護代理:被代理類控制哪些方法可以被訪問,哪些不能被訪問。
4.緩存代理:為開銷大的運算結(jié)果提供暫時存儲,它允許多個客戶共享結(jié)果,以減少計算或網(wǎng)絡延遲。
代理模式代理模式能幫助我們完成什么?
舉個生活中常見的例子:在我們需要租房時,通常都要去房屋中介登記、看房,此時帶我們?nèi)タ捶康木裥』锞褪欠恐鞯奈腥?,房主告訴他想要租2000塊一個月,他接受了委派并且自己想要拿到一些額外的傭金,在原本出租價格的基礎上加了200塊。此時,我們租房要花2200,我們不知道房東是誰,也不知道房東要租多少錢,我們只能跟這個精神小伙打交道,他就是房主的代理,價錢合適就讓房主來簽合同。
有動態(tài)代理模式的概念,自然有靜態(tài)代理模式,我們先看一下靜態(tài)代理的實現(xiàn)代碼:
import java.math.BigDecimal;
/**
* @Author:zhangyanan
* @Description: 房屋出租Service
* @Date:Crated in 17:29 2020/3/12
* @Modified By:
*/
public interface HouseOwnerService {
/**
* 出租房屋的價格
*/
BigDecimal rentalHousePrice();
}
上圖為提供服務的接口
import java.math.BigDecimal;
/**
* @Author:zhangyanan
* @Description: 服務實現(xiàn)
* @Date:Crated in 17:29 2020/3/12
* @Modified By:
*/
public class HouseOwnerServiceImpl implements HouseOwnerService {
/**
* 出租房屋的價格
*/
@Override
public BigDecimal rentalHousePrice() {
return new BigDecimal("2000");
}
}
上圖為服務的接口實現(xiàn)
import java.math.BigDecimal;
/**
* @Author:zhangyanan
* @Description: 房屋中介代理類
* @Date:Crated in 19:31 2020/3/12
* @Modified By:
*/
public class LettingAgent {
private leaseService leaseService;
public LettingAgent(leaseService leaseService) {
this.leaseService = leaseService;
}
/**
* 告訴租戶的房屋出租價格
*/
public void printRentalHosuse() {
System.out.println("房主想租的價格是:" + leaseService.rentalHousePrice().toString());
//精神小伙又自己加了200塊錢
System.out.println("精神小伙想出租的價格是:" + leaseService.rentalHousePrice().add(new BigDecimal("200")).toString());
}
}
上圖為精神小伙代理類,額外增加了自己的傭金
/**
* @Author:zhangyanan
* @Description:
* @Date:Crated in 10:25 2019/9/23
* @Modified By:
*/
public class Test {
public static void main(String[] args) {
leaseService service = new leaseServiceImpl();
LettingAgent lettingAgent = new LettingAgent(service);
lettingAgent.printRentalHosuse();
}
}
調(diào)用代理類,打印結(jié)果:
房主想租的價格是:2000
精神小伙想出租的價格是:2200
是不是看著有點像行為模式,他們之間看似結(jié)構(gòu)相同,但實際目的不同。裝飾模式是為對象加上一些行為,而代理模式則是控制訪問。
靜態(tài)代理模式的缺點:每當需要一個代理類就要新創(chuàng)建一個代理類,將被代理類放進構(gòu)造方法。強耦合,代碼依賴寫死,必須顯示的創(chuàng)建代理對象,會造成設計的類不斷增加。
動態(tài)代理模式如何實現(xiàn)?
JDK為我們提供了內(nèi)部實現(xiàn)動態(tài)代理的方式,有幾個兩個重要的類:
1.Proxy:代理類,調(diào)用Proxy.newInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)工廠方法創(chuàng)建實例。
2.InvocationHandler:代理輔助類,代理調(diào)用任何方法都會執(zhí)行InvocationHandler.invoke()。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.math.BigDecimal;
/**
* @Author:zhangyanan
* @Description:
* @Date:Crated in 19:45 2020/3/12
* @Modified By:
*/
public class Hander implements InvocationHandler {
private leaseService service;
public Hander(leaseService service) {
this.service = service;
}
/**
* Proxy動態(tài)代理類調(diào)用所有的方法都會經(jīng)過invoke方法
*
* @param proxy 調(diào)用的代理對象
* @param method 被調(diào)用的方法
* @param args 方法參數(shù)
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("**********進入handler.invoke()方法**********");
if ("rentalHousePrice".equals(method.getName())) {
System.out.println("我是精神小伙,這次我想加500作為我的傭金");
BigDecimal price = ((BigDecimal) method.invoke(service, args)).add(new BigDecimal("500"));
System.out.println("此小區(qū)的租房的價格是:" + price);
}
return null;
}
}
上圖為代理對象的處理類實現(xiàn)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
/**
* @Author:zhangyanan
* @Description:
* @Date:Crated in 10:25 2019/9/23
* @Modified By:
*/
public class Test {
public static void main(String[] args) {
leaseService service = new leaseServiceImpl();
InvocationHandler handler = new Hander(service);
//利用工廠創(chuàng)建動態(tài)代理實例
leaseService proxyService = (leaseService) Proxy.newProxyInstance(service.getClass().getClassLoader(), service.getClass().getInterfaces(), handler);
//調(diào)用代理方法
proxyService.rentalHousePrice();
}
}
測試結(jié)果:
**********進入handler.invoke()方法**********
我是精神小伙,這次我想加500作為我的傭金
此小區(qū)的租房的價格是:2500
動態(tài)代理模式的優(yōu)點:低耦合,在運行時創(chuàng)建對象,有興趣的小伙伴們可以看一下創(chuàng)建動態(tài)代理的源碼:

image.png
簡單來說一下動態(tài)代理的創(chuàng)建過程:
1.獲得真實服務實現(xiàn)類的所有接口和類加載器,獲得所有的接口就能獲得接口所有的方法。
2.得到動態(tài)代理類的Class。
3.獲得動態(tài)對象的構(gòu)造方法。
4.將handler處理類放入動態(tài)代理類的有參構(gòu)造器。
5.創(chuàng)建代理對象。