天天看點

23天讀懂23種設計模式:代理模式(結構型)

結構型模式讨論的是類和對象的結構,它采用繼承機制來組合接口或實作(類結構型模式),或者通過組合一些對象實作新的功能(對象結構型模式)。代理模式的目的:為其他對象提供一種代理以控制對該對象的通路,即用一個類去代表另一個類的功能。

winter

代理模式也是結構型的設計模式之一,本文是設計模式系列(共24節)的第7篇文章。設計模式都從六大原則出發進行總結:《第一節:設計模式的六大原則》結構型設計模式共7種:

代理模式(Proxy Pattern):為其他對象提供一種代理以控制對該對象的通路。

裝飾模式(Decorator Pattern):動态的給一個對象添加一些額外的職責。就增加功能來說,裝飾模式比生成子類更靈活。

擴充卡模式(Adapter Pattern):将一個類的接口變換成用戶端所期待的接口,進而使原本因接口不比對而無法在一起工作的兩個類能夠在一起工作。

組合模式(Composite Pattern):也叫合成模式,将對象組合成屬性結構一表示“部分-整體”的層次結構,使得使用者對單個對象群組成對象的使用具有一緻性。

橋梁模式(Bridge Pattern):也叫橋接模式,将抽象和實作解耦,是的兩者可以獨立的變化。

外觀模式(Facade Pattern):也叫門面模式,要求一個子系統的外部與其内部的通信必須通過一個統一的對象進行,外觀模式提供一個高層次的接口,使得子系統更易于使用。

亨元模式(Flyweight Pattern):是池技術的重要實作方式,使用共享對象可有效的支援大量的細粒度的對象

23天讀懂23種設計模式:代理模式(結構型)
23天讀懂23種設計模式:代理模式(結構型)

代理模式是什麼?

代理模式分兩種:

靜态代理:構造不同的實體類,顯式傳參給代理類進行方法調用。

動态代理:入參是對象和注解名,重新構造被代理對象。

23天讀懂23種設計模式:代理模式(結構型)
23天讀懂23種設計模式:代理模式(結構型)
23天讀懂23種設計模式:代理模式(結構型)

靜态代理實作

靜态代理的特點:代理類接受一個Subject接口的對象,任何實作該接口的對象,都可以通代理類進行代理,增加了通用性。

1、定義被代理類:UserDao 和 WomanDao,兩者都實作了 Subject 公共接口。

2、定義代理類:ProxyHuman

3、代理Demo:

代碼分析:

23天讀懂23種設計模式:代理模式(結構型)

缺點:1)每一個代理類都必須實作一遍委托類(也就是realsubject)的接口,如果接口增加方法,則代理類也必須跟着修改;2)代理類每一個接口對象對應一個委托對象,如果委托對象非常多,則靜态代理類就非常臃腫,難以勝任。

23天讀懂23種設計模式:代理模式(結構型)

動态代理實作:JDK實作

JDK的動态代理是基于反射實作。

JDK通過反射生成的代理類實作了原來那個類的所有接口,并對接口的方法進行了代理。(此處我是有些雲裡霧裡了..繼續看下去吧)

JDK實作動态代理需要兩個元件:

1、首先是定義公共業務接口:

2、元件一:編寫一個代理攔截器類,并實作InvocationHandler接口。我們在使用JDK的動态代理時,需要,去實作這個接口,然後重寫invoke方法,這個方法其實就是我們提供的代理方法。

3、元件二:使用Proxy這個類,通過靜态方法Proxy.newProxyInstance()方法,傳回一個代理對象。

其實,這個代理對象在底層是會被JVM編譯為一個新的類對象( $Proxy0),它是由Proxy這個類通過newProxyInstance方法動态生成的。

JDK動态生成代理類

我們demo裡面有提示:将動态生成的代理類的.class檔案儲存在本地。

我們打開本地儲存的這個class檔案,可以看到源碼,走讀一下代碼:

1、$Proxy0 繼承了 Proxy 類,并且實作了公共接口 IUserDao;

2、find() 方法最終觸發我們的 JDKDynamicProxy 的 invoke() 方法;

是以,當通過代理對象調用這些方法時,底層将通過反射,調用我們實作的invoke方法(即:IUserDao的find()方法調用,會觸發我們 JDKDynamicProxy 的 invoke() 方法。)

優點:依賴JDK,更穩定可靠,跟着JDK更新,代碼簡單。

缺點:要求被代理類必須要有實作的接口,否則無法使用JDK動态代理(從newProxyInstance方法的第二個參數可得知,必須傳入被代理類的實作接口),而這個解決方法便是使用CGLib動态代理。

Spring預設使用JDK的動态代理實作AOP,類如果實作了接口,Spring就會使用這種方式實作動态代理。熟悉Java語言的應該會對JDK動态代理有所了解。
23天讀懂23種設計模式:代理模式(結構型)

動态代理實作:CGlib實作

JDK 動态代理隻能為接口中的方法完成代理,而委托類自己的方法或者父類中的方法都不可能被代理。

CGLIB 應運而生,它是一個高性能的,底層基于 ASM 架構的一個代碼生成架構,它完美的解決了 JDK 版本的動态代理隻能為接口方法代理的單一性不足問題。Cglib動态代理采用的是建立目标類的子類的方式。

    下面我們看下Cglib的動态代理例子:

1、編寫CGlib 動态代理類攔截規則類:實作CGLIB動态代理必須實作MethodInterceptor(方法攔截器)接口。

這個接口隻有一個intercept()方法,這個方法有4個參數:  1)obj表示增強的對象,即實作這個接口類的一個對象;  2)method表示要被攔截的方法;  3)args表示要被攔截方法的參數; 4)proxy表示要觸發父類的方法對象;

我們自定義的動态代理攔截器類 CGlibMyMethodInterceptor:

2、使用Cglib提供的工具類實作動态代理:通過Enhancer.create()方法建立代理對象;

輸出動态代理類代碼:

23天讀懂23種設計模式:代理模式(結構型)

CGlib動态生成代理類

同樣我們可以走讀下Cglib生産的動态代理類代碼:1、動态代理類直接繼承了 被代理對象 SubjectA/SubjectB,同時實作了Factory接口;

2、動态代理類覆寫了 find() 方法,同時也是我們的重點關注代理對象方法。

代碼分析:走讀代理對象反編譯源碼可以知道,代理對象繼承于 SubjectA,攔截器調用intercept()方法,intercept()方法由自定義 CGlibMyMethodInterceptor 實作。是以,最後調用 CGlibMyMethodInterceptor 中的intercept()方法,進而完成了由代理對象通路到目标對象的動态代理實作。好處:不用實作額外接口,隻操作我們關心類,也額外提高了性能。

JDK的動态代理存在限制,那就是被代理的類必須是一個實作了接口的類,代理類需要實作相同的接口,代理接口中聲明的方法。若需要代理的類沒有實作接口,此時JDK的動态代理将沒有辦法使用,于是Spring會使用CGLib的動态代理來生成代理對象:因為CGLib直接操作位元組碼,生成類的子類,重寫類的方法完成代理。
23天讀懂23種設計模式:代理模式(結構型)

JDK動态代理與CGLib動态代理的差別

JDK動态代理模式和CGLib動态代理的應用差別:

JDK動态代理需要被代理類實作接口,而CGLib則是生成被代理類的子類;

JDK動态代理模式和CGLib動态代理的性能差別:

<code>CGLib動态代理建立代理執行個體速度慢,但是運作速度快;JDK動态代理建立執行個體速度快,但是運作速度慢。如果執行個體是單例的,</code><code>推薦使用CGLib方式動态代理,反之則使用JDK方式進行動态代理。</code>

<code>Spring的執行個體預設是單例,是以這時候使用CGLib性能高。</code>

在1.6和1.7的時候,JDK動态代理的速度要比CGLib動态代理的速度要慢,但是并沒有教科書上的10倍差距,在JDK1.8的時候,JDK動态代理的速度已經比CGLib動态代理的速度快很多了。
23天讀懂23種設計模式:代理模式(結構型)

總結

總結一下,文章概要分析了代理模式下的動态代理和靜态代理兩種實作方式;同時由于動态代理有JDK和Cglib兩種工具,是以也做了簡要分析和比較。

後面我們了解Spring AOP,涉及到的JDK和CGLib動态代理便是與本文有關,後續我們再細細分析。

掃描二維碼

擷取技術幹貨

背景技術彙

23天讀懂23種設計模式:代理模式(結構型)