代理模式(Proxy Pattern)是指由于某些原因需要給某個對象提供一個代理以控制對該對象的訪問,這時訪問對象不適合或者不能直接訪問目標對象,使用代理對象作為目標對象和訪問對象之間的中介
現(xiàn)實中的例子
房屋中介,律師,婚姻介紹所等...
代理模式的結(jié)構(gòu)與特點
代理模式的主要角色
1.抽象主題(Abstract Subject)角色,通過接口或者抽象類聲明真實主題和代理對象實現(xiàn)的業(yè)務方法
2.真實主題(Real Subject)角色,實現(xiàn)了抽象主題中的業(yè)務方法,是代理對象所代表的真實對象,是最終要被應用的對象
3.代理(Proxy)角色,提供了與真實主題相同的接口,其內(nèi)部含有對真實主題的引用,可以訪問,控制,和擴展真實主題的功能
代理模式的優(yōu)缺點
優(yōu)點
1.代理對象在訪問對象和目標對象直接建立一個中介的作用和保護目標對象的作用
2.帶對象可以擴展目標對象的功能
3.代理模式將客戶端和目標對象進行分離,在一定程度上降低了系統(tǒng)的耦合度
缺點
1.在客戶端和目標對象之間建立了一個代理對象,會造成訪問速度的變慢
2.增加了系統(tǒng)的復雜度
靜態(tài)代理
創(chuàng)建抽象主題,提供真實主題和代理對象實現(xiàn)的業(yè)務方法
/**
* 抽象主題 聲明真實主題和代理主題必須實現(xiàn)的接口
*/
public interface Business {
public void shoping();
}
創(chuàng)建真實主題,實現(xiàn)抽象主題的業(yè)務方法
/**
* 真實主題 實現(xiàn)抽象主圖的方法,是代理對象所代表的真實對象
*/
public class Client implements Business{
@Override
public void shoping() {
System.out.println("用戶買了一個刮胡刀");
}
}
創(chuàng)建代理類,實現(xiàn)抽象主題的的業(yè)務方法,并持有對真實主題的引用
/**
* 代理主題 快遞類,實現(xiàn)抽象主題的方法,其內(nèi)部持有真實主題的引用,可以擴展真實主題的功能
*/
public class ExpressProxy implements Business{
// 真實主題的引用
private Business client;
public ExpressProxy(Business client){
this.client = client;
}
@Override
public void shoping() {
before();
this.client.shoping();
after();
}
// 功能擴展
private void before(){
System.out.println("接到訂單通知");
}
// 功能擴展
public void after(){
System.out.println("訂單已經(jīng)發(fā)貨...");
}
}
測試
public static void main(String[] args) {
// 創(chuàng)建代理類,并傳入真實主題的引用
Business business = new ExpressProxy(new Client());
business.shoping();
}
結(jié)果
接到訂單通知
用戶買了一個刮胡刀
訂單已經(jīng)發(fā)貨...
靜態(tài)代理類的弊端
1.目標類和代理類必須實現(xiàn)同一個接口
2.目標類增多代理類也會隨之增多
動態(tài)代理
JDK 動態(tài)代理
創(chuàng)建 Persion,提供找對象的方法
/**
* 抽象主題 提供真實主題要實現(xiàn)的業(yè)務邏輯
*/
public interface IPersion {
public void findLove();
}
分別創(chuàng)建真實主題,ZhangSan,LiSi 類,實現(xiàn)抽象主題的方法
/**
* 真實主題 實現(xiàn)抽象主題的方法 是被代理的目標類
*/
public class ZhangSan implements IPersion{
@Override
public void findLove() {
System.out.println("要求:膚白貌美大長腿...");
}
}
/**
* 真實主題 實現(xiàn)抽象主題的方法 是最終被代理的類
*/
public class LiSi implements IPersion{
@Override
public void findLove() {
System.out.println("要求:學歷高,顏值高,個字高...");
}
}
創(chuàng)建代理媒婆類,實現(xiàn) JDK ,Invocationhandler 接口
/**
* jdk 代理類 是有目標對象的引用
*/
public class JDKMeiPo implements InvocationHandler {
// 目標對象
private Object target;
public Object getProxy(Object target){
this.target = target;
return Proxy.newProxyInstance(this.getClass().getClassLoader(),this.target.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 擴展方法
before();
method.invoke(this.target,args);
// 擴展方法
after();
return null;
}
// 擴展方法
private void after() {
System.out.println("如果合適的話,就結(jié)婚吧");
}
// 擴展方法
private void before() {
System.out.println("我是媒婆,開始我要給你找對象,現(xiàn)在已經(jīng)確認要求");
System.out.println("開始物色...");
}
}
測試
public static void main(String[] args) {
System.out.println("===============給張三找對象================");
JDKMeiPo zsProxy = new JDKMeiPo();
IPersion zs = (IPersion) zsProxy.getProxy(new ZhangSan());
zs.findLove();
System.out.println("===============給李四找對象================");
JDKMeiPo lsProxy = new JDKMeiPo();
IPersion ls = (IPersion) lsProxy.getProxy(new LiSi());
ls.findLove();
}
結(jié)果
===============給張三找對象================
我是媒婆,開始我要給你找對象,現(xiàn)在已經(jīng)確認要求
開始物色...
要求:膚白貌美大長腿...
如果合適的話,就結(jié)婚吧
===============給李四找對象================
我是媒婆,開始我要給你找對象,現(xiàn)在已經(jīng)確認要求
開始物色...
要求:學歷高,顏值高,個字高...
如果合適的話,就結(jié)婚吧
CGLib 動態(tài)代理
加入 CGlib jar 包
<dependencies>
<!-- cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>
構(gòu)建 CGLIB 類,實現(xiàn) MethodInterceptor 中的接口
/**
* CJlib 動態(tài)代理
*/
public class CGLibMeiPo implements MethodInterceptor {
/**
* 生成Cglib代理對象
* Class 真實對象的class對象
* @return Cglib代理對象
*/
public Object getProxy(Class<?> clazz){
// Cglib 的增強類對象
Enhancer enhancer = new Enhancer();
// 設(shè)置增強的類型,真實類的類型
enhancer.setSuperclass(clazz);
// 定義代理邏輯為當前對象,要求當前對象實現(xiàn) MethodInterceptor 中的接口
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object obj = methodProxy.invokeSuper(o, objects);
after();
return obj;
}
// 擴展方法
private void after() {
System.out.println("如果合適的話,就結(jié)婚吧");
}
// 擴展方法
private void before() {
System.out.println("我是媒婆,開始我要給你找對象,現(xiàn)在已經(jīng)確認要求");
System.out.println("開始物色...");
}
}
測試結(jié)果和 jdk 動態(tài)代理一樣,結(jié)果也一樣
CGLIB 和 JDK 動態(tài)代理的對比
1.JDK 動態(tài)代理實現(xiàn)了被代理對象的接口,CGLIB 代理類繼承了被代理對象
2.JDK 動態(tài)代理和 CGLIB 代理都在運行時期生成類的字節(jié)碼,JDK動態(tài)代理直接寫 Classs 字節(jié)碼,CGLIB代理使用 ASM 框架寫 Class 字節(jié)碼,CGLIB 代理實現(xiàn)更復雜,生成的代理對象比 JDK 動態(tài)代理效率低
3.JDK 動態(tài)代理動用方法是通過反射機制調(diào)用的,CGLIB 代理是通過 FastClass 機制直接調(diào)用方法的,CGLIB 代理的執(zhí)行效率更高