代理模式
? 代理模式在 Java 開發(fā)中是一種比較常見的設(shè)計(jì)模式六水。設(shè)計(jì)目的旨在為服務(wù)類與客戶類之間插入其他功能,插入的功能對于調(diào)用者是透明的辣卒,起到偽裝控制的作用缩擂。如租房的例子:房客、中介添寺、房東。對應(yīng)于代理模式中即:客戶類懈费、代理類 计露、委托類(被代理類)。
? 為某一個(gè)對象(委托類)提供一個(gè)代理(代理類)憎乙,用來控制對這個(gè)對象的訪問票罐。委托類和代理類有一個(gè)共同的父類或父接口。代理類會對請求做預(yù)處理泞边、過濾该押,將請求分配給指定對象。
? 生活中常見的代理情況:
? 租房中介阵谚、婚慶公司等
? 代理模式的兩個(gè)設(shè)計(jì)原則:
? 1. 代理類 與 委托類 具有相似的行為(共同)
? 2. 代理類增強(qiáng)委托類的行為
? 常用的代理模式:
? 1. 靜態(tài)代理
? 2. 動態(tài)代理
靜態(tài)代理
? 某個(gè)對象提供一個(gè)代理蚕礼,代理角色固定,以控制對這個(gè)對象的訪問梢什。 代理類和委托類有共同的父類或父接口奠蹬,這樣在任何使用委托類對象的地方都可以用代理對象替代。代理類負(fù)責(zé)請求的預(yù)處理嗡午、過濾囤躁、將請求分派給委托類處理、以及委托類執(zhí)行完請求后的后續(xù)處理。
代理的三要素
? a狸演、有共同的行為(結(jié)婚) - 接口
? b言蛇、目標(biāo)角色(新人) - 實(shí)現(xiàn)行為
? c、代理角色(婚慶公司) - 實(shí)現(xiàn)行為 增強(qiáng)目標(biāo)對象行為
靜態(tài)代理的特點(diǎn)
? 1宵距、目標(biāo)角色固定
? 2腊尚、在應(yīng)用程序執(zhí)行前就得到目標(biāo)角色
? 3、代理對象會增強(qiáng)目標(biāo)對象的行為
? 4消玄、有可能存在多個(gè)代理 引起"類爆炸"(缺點(diǎn))
靜態(tài)代理的實(shí)現(xiàn)
定義行為(共同) 定義接口
/**
* 定義行為
*/
public interface Marry {
public void toMarry();
}
目標(biāo)對象(實(shí)現(xiàn)行為)
/**
* 靜態(tài)代理 ——> 目標(biāo)對象
*/
public class You implements Marry {
// 實(shí)現(xiàn)行為
@Override
public void toMarry() {
System.out.println("我要結(jié)婚了...");
}
}
代理對象(實(shí)現(xiàn)行為跟伏、增強(qiáng)目標(biāo)對象的行為)
/**
* 靜態(tài)代理 ——> 代理對象
*/
public class MarryCompanyProxy implements Marry {
// 目標(biāo)對象
private Marry marry;
// 通過構(gòu)造器將目標(biāo)對象傳入
public MarryCompanyProxy(Marry marry) {
this.marry = marry;
}
// 實(shí)現(xiàn)行為
@Override
public void toMarry() {
// 增強(qiáng)行為
before();
// 執(zhí)行目標(biāo)對象中的方法
marry.toMarry();
// 增強(qiáng)行為
after();
}
/**
* 增強(qiáng)行為
*/
private void after() {
System.out.println("新婚快樂,早生貴子翩瓜!");
}
/**
* 增強(qiáng)行為
*/
private void before() {
System.out.println("場地正在布置中...");
}
}
通過代理對象實(shí)現(xiàn)目標(biāo)對象的功能
// 目標(biāo)對象
You you = new You();
// 構(gòu)造代理角色同時(shí)傳入真實(shí)角色
MarryCompanyProxy marryCompanyProxy = new MarryCompanyProxy(you);
// 通過代理對象調(diào)用目標(biāo)對象中的方法
marryCompanyProxy.toMarry();
? 靜態(tài)代理對于代理的角色是固定的受扳,如dao層有20個(gè)dao類,如果要對方法的訪問權(quán)限進(jìn)行代理兔跌,此時(shí)需要?jiǎng)?chuàng)建20個(gè)靜態(tài)代理角色勘高,引起類爆炸,無法滿足生產(chǎn)上的需要坟桅,于是就催生了動態(tài)代理的思想华望。
如有疑問,可加入群:10803-55292,輸入暗號13仅乓,即可有大佬幫助
動態(tài)代理
? 相比于靜態(tài)代理赖舟,動態(tài)代理在創(chuàng)建代理對象上更加的靈活,動態(tài)代理類的字節(jié)碼在程序運(yùn)行時(shí)夸楣,由Java反射機(jī)制動態(tài)產(chǎn)生宾抓。它會根據(jù)需要,通過反射機(jī)制在程序運(yùn)行期豫喧,動態(tài)的為目標(biāo)對象創(chuàng)建代理對象石洗,無需程序員手動編寫它的源代碼。動態(tài)代理不僅簡化了編程工作紧显,而且提高了軟件系統(tǒng)的可擴(kuò)展性讲衫,因?yàn)榉瓷錂C(jī)制可以生成任意類型的動態(tài)代理類。代理的行為可以代理多個(gè)方法孵班,即滿足生產(chǎn)需要的同時(shí)又達(dá)到代碼通用的目的涉兽。
? 動態(tài)代理的兩種實(shí)現(xiàn)方式:
? 1. JDK 動態(tài)代理
? 2. CGLIB動態(tài)代理
動態(tài)代理的特點(diǎn)
- 目標(biāo)對象不固定
- 在應(yīng)用程序執(zhí)行時(shí)動態(tài)創(chuàng)建目標(biāo)對象
- 代理對象會增強(qiáng)目標(biāo)對象的行為
JDK動態(tài)代理
? 注:JDK動態(tài)代理的目標(biāo)對象必須有接口實(shí)現(xiàn)
newProxyInstance
Proxy類:
? Proxy類是專門完成代理的操作類,可以通過此類為一個(gè)或多個(gè)接口動態(tài)地生成實(shí)現(xiàn)類重父,此類提供了如下操作方法:
/*
返回一個(gè)指定接口的代理類的實(shí)例方法調(diào)用分派到指定的調(diào)用處理程序花椭。 (返回代理對象)
loader:一個(gè)ClassLoader對象,定義了由哪個(gè)ClassLoader對象來對生成的代理對象進(jìn)行加載
interfaces:一個(gè)Interface對象的數(shù)組房午,表示的是我將要給我需要代理的對象提供一組什么接口矿辽,如果 我提供了一組接口給它,那么這個(gè)代理對象就宣稱實(shí)現(xiàn)了該接口(多態(tài)),這樣我就能調(diào)用這 組接口中的方法了
h:一個(gè)InvocationHandler接口袋倔,表示代理實(shí)例的調(diào)用處理程序?qū)崿F(xiàn)的接口雕蔽。每個(gè)代理實(shí)例都具有一個(gè) 關(guān)聯(lián)的調(diào)用處理程序。對代理實(shí)例調(diào)用方法時(shí)宾娜,將對方法調(diào)用進(jìn)行編碼并將其指派到它的調(diào)用處理程序 的 invoke 方法(傳入InvocationHandler接口的子類)
*/
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
獲取代理對象
public class JdkHandler implements InvocationHandler {
// 目標(biāo)對象
private Object target; // 目標(biāo)對象的類型不固定批狐,創(chuàng)建時(shí)動態(tài)生成
// 通過構(gòu)造器將目標(biāo)對象賦值
public JdkHandler(Object target) {
this.target = target;
}
/**
* 1、調(diào)用目標(biāo)對象的方法(返回Object)
* 2前塔、增強(qiáng)目標(biāo)對象的行為
* @param proxy 調(diào)用該方法的代理實(shí)例
* @param method 目標(biāo)對象的方法
* @param args 目標(biāo)對象的方法形參
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 增強(qiáng)行為
System.out.println("==============方法前執(zhí)行");
// 調(diào)用目標(biāo)對象的方法(返回Object)
Object result = method.invoke(target,args);
// 增強(qiáng)行為
System.out.println("方法后執(zhí)行==============");
return result;
}
/**
* 得到代理對象
* public static Object newProxyInstance(ClassLoader loader,
* Class<?>[] interfaces,
* InvocationHandler h)
* loader:類加載器
* interfaces:接口數(shù)組
* h:InvocationHandler接口 (傳入InvocationHandler接口的實(shí)現(xiàn)類)
*
*
* @return
*/
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
}
通過代理對象實(shí)現(xiàn)目標(biāo)對象的功能
// 目標(biāo)對象
You you = new You();
// 獲取代理對象
JdkHandler jdkHandler = new JdkHandler(you);
Marry marry = (Marry) jdkHandler.getProxy();
// 通過代理對象調(diào)用目標(biāo)對象中的方法
marry.toMarry();
問:Java動態(tài)代理類中的invoke是怎么調(diào)用的嚣艇?
答:在生成的動態(tài)代理類$Proxy0.class中,構(gòu)造方法調(diào)用了父類Proxy.class的構(gòu)造方法华弓,給成員變量invocationHandler賦值食零,$Proxy0.class的static模塊中創(chuàng)建了被代理類的方法,調(diào)用相應(yīng)方法時(shí)方法體中調(diào)用了父類中的成員變量InvocationHandler的invoke()方法寂屏。
注:JDK的動態(tài)代理依靠接口實(shí)現(xiàn)贰谣,如果有些類并沒有接口實(shí)現(xiàn),則不能使用JDK代理迁霎。
CGLIB 動態(tài)代理
? JDK的動態(tài)代理機(jī)制只能代理實(shí)現(xiàn)了接口的類吱抚,而不能實(shí)現(xiàn)接口的類就不能使用JDK的動態(tài)代理,cglib是針對類來實(shí)現(xiàn)代理的考廉,它的原理是對指定的目標(biāo)類生成一個(gè)子類秘豹,并覆蓋其中方法實(shí)現(xiàn)增強(qiáng),但因?yàn)椴捎玫氖抢^承昌粤,所以不能對final修飾的類進(jìn)行代理憋肖。
添加依賴
在pom.xml文件中引入cglib的相關(guān)依賴
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
定義類
實(shí)現(xiàn)MethodInterceptor接口
public class CglibInterceptor implements MethodInterceptor {
// 目標(biāo)對象
private Object target;
// 通過構(gòu)造器傳入目標(biāo)對象
public CglibInterceptor(Object target) {
this.target = target;
}
/**
* 獲取代理對象
* @return
*/
public Object getProxy() {
// 通過Enhancer對象的create()方法可以生成一個(gè)類,用于生成代理對象
Enhancer enhancer = new Enhancer();
// 設(shè)置父類 (將目標(biāo)類作為其父類)
enhancer.setSuperclass(target.getClass());
// 設(shè)置攔截器 回調(diào)對象為本身對象
enhancer.setCallback(this);
// 生成一個(gè)代理類對象婚苹,并返回
return enhancer.create();
}
/**
* 攔截器
* 1、目標(biāo)對象的方法調(diào)用
* 2鸵膏、增強(qiáng)行為
* @param object 由CGLib動態(tài)生成的代理類實(shí)例
* @param method 實(shí)體類所調(diào)用的被代理的方法引用
* @param objects 參數(shù)值列表
* @param methodProxy 生成的代理類對方法的代理引用
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object object, Method method, Object[] objects,
MethodProxy methodProxy) throws Throwable {
// 增強(qiáng)行為
System.out.println("==============方法前執(zhí)行");
// 調(diào)用目標(biāo)對象的方法(返回Object)
Object result = methodProxy.invoke(target,objects);
// 增強(qiáng)行為
System.out.println("方法后執(zhí)行==============");
return result;
}
}
調(diào)用方法
// 目標(biāo)對象
You you = new You();
CglibInterceptor cglibInterceptor = new CglibInterceptor(you);
Marry marry = (Marry) cglibInterceptor.getProxy();
marry.toMarry();
User user = new User();
CglibInterceptor cglibInterceptor = new CglibInterceptor(user);
User u = (User) cglibInterceptor.getProxy();
u.test();
JDK代理與CGLIB代理的區(qū)別
- JDK動態(tài)代理實(shí)現(xiàn)接口膊升,Cglib動態(tài)代理繼承思想
- JDK動態(tài)代理(目標(biāo)對象存在接口時(shí))執(zhí)行效率高于Ciglib
- 如果目標(biāo)對象有接口實(shí)現(xiàn),選擇JDK代理谭企,如果沒有接口實(shí)現(xiàn)選擇Cglib代理