今天要開始我們結構型
設計模式的學習,設計模式源於生活,還是希望能通過生活中的一些小栗子去理解學習它,而不是為了學習而學習這些東西。
結構型設計模式
結構型設計模式又分為
- 類 結構型
- 物件 結構型
前者使用物件的
繼承機制
來組織物件和類
後者採用組合聚合
的方式來組合物件。
代理模式 Proxy
理解代理一詞
代理表達的是:為某個物件提供一種代理,用於控制該物件的訪問,讓客戶端間接的訪問該物件,從而限制、或者增強源物件的一些特性。
舉個例子
從國內科學SW
,訪問谷歌查閱一些資料的時候,我們肯定務必會藉助一些代理器
也就是通常所說的VPN
,代理的伺服器可以幫助我們完成這些操作。
靜態代理
畫個圖理解一下
需要說明的地方有:
- 抽象父類或者介面:定義了這個代理可以代理的方法。比如定義了一個
SearchSubject
實現它的子類必須要實現對應的search()
方法。
/**
* 抽象主題,可以進行搜尋
*/
public abstract class SearchSubject {
/**
* 可以進行搜尋的操作
*/
public abstract void search();
}
- 真實物件:真實物件也就是具體將要
被代理的方法
,這個真實物件的方法我們要通過代理類間接的去訪問
。
眾所周知,國內訪問不到Google,需要代理才行。
public class Google extends SearchSubject {
@Override
public void search() {
System.out.println("Google 搜尋引擎");
}
}
- 代理類:也就是VPN ,幫助我們訪問
真實物件
的某些方法,並且還可以做一些增強。比如在訪問真實物件之前
做一些事情,之後做一些事情。
/**
* VPN 代理
* 靜態代理也需要實現抽象主題
*/
public class VPNProxy extends SearchSubject {
/**
* 含有真實主題
*/
private Google google;
@Override
public void search() {
if (null == google) {
google = new Google();
}
this.before();
/**
* 呼叫真實物件的方法
*/
google.search();
this.after();
}
/**
* 增強方法
*/
public void before() {
System.out.println("VPN 開始執行。。。");
}
public void after() {
System.out.println("VPN 結束執行");
}
}
執行呼叫代理
VPNProxy proxy = new VPNProxy();
proxy.search();
------------------
VPN 開始執行。。。
Google 搜尋引擎
VPN 結束執行
以上就是我們要學習的第一種代理方式:靜態代理
動態代理
假設我們還需要代理一個物件呢?比如必應
假設必應搜尋
我們國內訪問不到,必須使用代理的話,是不是又得重新建立兩個物件
- 真實物件
必應搜尋
- 代理物件
必應搜尋的代理
這就不利於我們系統的擴充套件性,假設有很多需要代理的,那豈不是寫一大堆。
因此,動態代理由此而生。
這裡我們使用JDK 提供的動態代理
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h){}
- ClassLoader 類載入器
- interfaces 載入的介面
- InvocationHandler 增強處理器以及呼叫代理的類
建立一個可供實現的搜尋介面
/**
* 搜尋介面
*/
public interface SearchInterface {
String search();
}
谷歌搜尋引擎實現了這個介面,並且將名稱作為返回值返回。
public class GoogleSearch implements SearchInterface {
@Override
public String search() {
System.out.println("Google 搜尋引擎");
return "Google";
}
}
建立一個搜尋增強器,並且建立了兩個方法的增強,在呼叫代理之前和之後,都加入了一些方法。
/**
* 搜尋處理器
*/
public class SearchHandler implements InvocationHandler {
private void before() {
System.out.println("handler start");
}
private void after() {
System.out.println("handler stop");
}
private SearchInterface obj;
public SearchHandler(SearchInterface obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
this.before();
/**
* 執行代理方法
*/
Object result = method.invoke(obj, args);
System.out.println("result=" + result.toString());
this.after();
return result;
}
}
建立一個動態代理工廠,將我們需要代理的介面傳入,並且傳入介面的處理類,即可實現介面的增強處理。
/**
* 動態代理工廠
*/
public class ProxyFactory {
/**
* 目標物件
*/
private SearchInterface object;
private InvocationHandler handler;
public ProxyFactory(SearchInterface obj, InvocationHandler handler) {
this.object = obj;
this.handler = handler;
}
/**
* 獲取代理物件
* @return
*/
public Object getProxyObj() {
ClassLoader classLoader = object.getClass().getClassLoader();
Class<?>[] interfaces = object.getClass().getInterfaces();
return Proxy.newProxyInstance(classLoader, interfaces, handler);
}
}
建立一個具體的介面物件,傳入我們的代理工廠,並且將其處理器也同時傳入,我們就可以得到一個代理物件。
SearchInterface search = new GoogleSearch();
System.out.println("1id=" + search);
InvocationHandler handler = new SearchHandler(search);
ProxyFactory factory = new ProxyFactory(search, handler);
SearchInterface google = (SearchInterface) factory.getProxyObj();
System.out.println("2id=" + google);
google.search();
-----------------
1id=impl.GoogleSearch@1b6d3586
handler start
result=impl.GoogleSearch@1b6d3586
handler stop
2id=impl.GoogleSearch@1b6d3586
handler start
Google 搜尋引擎
result=Google
handler stop
從上面的程式碼我們發現:
- 代理的物件與我們建立的物件有所不同
- 在生成代理物件的時候、已經執行了一遍invoke() 方法
- 通過代理物件呼叫具體方法的時候也執行了一遍invoke()
老衲畫個圖
這樣就好理解多了,代理工廠需要一個代理類、以及這個代理類的增強方法(處理器),通過代理工廠生成的代理物件,實現對物件的增強處理。
動態代理的總結
- 代理類不需要實現介面,但是具體物件還是需要實現介面。
Cglib代理
上面兩種代理,都是需要代理類、或者是具體的目標物件實現某個介面的基礎上出現的,假設沒有這個介面的顯示,我只想在某個具體的物件上加入增強的話,如何實現呢?
Cglib代理又被稱作子類代理
,就是代理一個具體的子類
因為Spring 已經引入了相關的Cglib 的依賴,我們直接在Spring 的環境下進行測試。
建立一個具體的子類。沒有實現任何的介面
public class BingSearch {
public void search() {
System.out.println("必應搜尋。。。");
}
}
建立類實現一個方法攔截器,其實名字就是這樣叫的。我們的代理物件,是通過工具類拿出來的。
public class ProxyFactory implements MethodInterceptor {
//維護目標物件
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
private void before() {
System.out.println("代理類前置處理。。");
}
private void after() {
System.out.println("代理類後置處理。。");
}
public Object getProxy() {
//1.工具類
Enhancer en = new Enhancer();
//2.設定父類
en.setSuperclass(target.getClass());
//3.設定回撥函式
en.setCallback(this);
//4.建立子類(代理物件)
return en.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
/**
* 執行方法
*/
this.before();
Object result = method.invoke(target, objects);
this.after();
return result;
}
}
在main 方法對一個具體的類進行增強代理。
public static void main(String[] args) {
ProxyFactory proxyFactory = new ProxyFactory(new BingSearch());
BingSearch bing = (BingSearch) proxyFactory.getProxy();
bing.search();
}
---------
代理類前置處理。。
必應搜尋。。。
代理類後置處理。。
小結
本節,將我們最常用的兩種代理模式進行了一些講解,其實最重要的是JDK動態代理
和Cglib 具體方法代理增強
。因為大家已經擁抱Spring 的懷抱了,這兩種代理還是很重要的,Spring的AOP 切面也是一種基於動態代理的方式實現。非常好用,在Spring 宣告式事務當中,一個註解即可搞定許多冗餘的程式設計式事務,這無不歸功於 強大的動態代理
鳴謝&參考
https://www.cnblogs.com/leeego-123/p/10995975.html
程式碼
https://gitee.com/mrc1999/Dev-Examples