設計模式------代理模式詳解

代理模式詳解

目錄

代理模式詳解

1、什麼是代理模式

2、結構圖

3、靜態代理實例

4、動態代理實例

5、JDK動態代理和Cglib的區別


1、什麼是代理模式

代理模式是指爲其他對象提供一種代理,以控制這個對象的訪問。代理對象在客戶端和目標對象之間起到一種中介的作用。他屬於結構型設計模式

舉個例子就是,想象一下我們生活中的購買火車票的情節,我們可以從官網上直接購買,也可以到售票的窗口購買,如果從官網購買,那麼這就可以認爲是一個代理,代理對象就是app。

當然了,我們也可以從售票廳旁邊的小超市,或者其他地方代售火車票的地方購買火車票,那麼這些代銷處就是代理了火車站的售票功能,那麼他們就是代理。
 

2、結構圖

  • 代理模式共分爲四種角色:
    • Subject(抽象主題角色):真實主題角色與代理主題角色的共同父類,可以是具體類、抽象類、接口,客戶端針對抽象主題角色編程
    • RealSubject(真實主題角色):實現具體業務方法的類,被代理主題角色調用
    • Proxy(代理主題角色):持有真實主題角色的引用,在調用真實主題角色的具體業務方法之前或者之後添加其它操作
  • 客戶端面對抽象主題角色編程,調用抽象主題角色實際上調用的是代理主題角色,代理

3、靜態代理實例

         下面代碼演示的內容:父親代理兒子找對象。

public interface IPersion {
    public void findWife();
}


/**
 * @description: 目標類
 * @author: zps
 * @create: 2020-05-02 23:13
 **/
public class Son implements IPersion {
    @Override
    public void findWife() {
        System.out.println("兒子找對象的要求:白富美");
    }
}


/**
 * @description: 代理類
 * @author: zps
 * @create: 2020-05-02 23:14
 **/
public class Father implements IPersion {

    private Son son;

    public Father(Son son){
        this.son = son;
    }

    @Override
    public void findWife() {
        System.out.println("開始幫兒子找對象");
        son.findWife();
        System.out.println("找到了對象");
    }
}

**
 * @description: 測試
 * @author: zps
 * @create: 2020-05-02 23:16
 **/
public class Test {
    public static void main(String args[]){
        Father father = new Father(new Son());
        father.findWife();
    }
}

運行結果:

開始幫兒子找對象
兒子找對象的要求:白富美
找到了對象

4、動態代理實例

對代理模式而言,一般來說,具體主題類與其代理類是一一對應的,這也是靜態代理的特點。但是,也存在這樣的情況:有N個主題類,但是代理類中的“預處理、後處理”都是相同的,僅僅是調用主題不同。那麼,若採用靜態代理,那麼必然需要手動創建N個代理類,這顯然讓人相當不爽。動態代理則可以簡單地爲各個主題類分別生成代理類,共享“預處理,後處理”功能,這樣可以大大減小程序規模,這也是動態代理的一大亮點。

  在動態代理中,代理類是在運行時期生成的。因此,相比靜態代理,動態代理可以很方便地對委託類的相關方法進行統一增強處理,如添加方法調用次數、添加日誌功能等等。動態代理主要分爲JDK動態代理和cglib動態代理兩大類,本文主要對JDK動態代理進行探討。

代碼演示:

public interface IPersion {
    public void findWife();
}


/**
 * @description: 目標類
 * @author: zps
 * @create: 2020-05-02 23:32
 **/
public class Zhangsan implements IPersion {
    @Override
    public void findWife() {
        System.out.println("白富美");
    }
}

/**
 * @description: 代理類
 * @author: zps
 * @create: 2020-05-02 23:33
 **/
public class JdkMeipo implements InvocationHandler {
    private IPersion target;
    public Object getInstance(IPersion target){
        this.target = target;
        Class<?> clazz = target.getClass();

        //返回一個動態代理對象
        return  Proxy.newProxyInstance(clazz.getClassLoader() ,
                clazz.getInterfaces() ,
                this);
    }
    /**
     * proxy	最終生成的代理實例
     * method	被代理目標實例的某個具體方法,通過它可以發起目標實例方法的反射調用。
     * args	被代理實例某個方法的入參,在方法反射調用時使用。
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("找對象中");
        Object obj = method.invoke(this.target , args);
        System.out.println("找到對象了");
        return obj;
    }
}


public class Test {
    public static void main(String args[]){
          JdkMeipo jdkMeipo = new JdkMeipo();
          IPersion zhangsan = (IPersion) jdkMeipo.getInstance(new Zhangsan());
          zhangsan.findWife();
    }
}

運行結果:

找對象中
白富美
找到對象了

5、JDK動態代理和Cglib的區別

不僅知其然,還得知其所以然。既然JDK動態代理功能如此強大,那麼它是如何實現的呢?我們現 在來探究一下原理。

 我們都知道JDK動態代理採用字節重組,重新生成對象來替代原始對象,以達到動態代理的目的

JDK動態代理生成對象的步驟如下:

             (1)獲取被代理對象的引用並且獲取它的所有接口,反射獲取。

             (2)JDK動態代理類重新生成一個新的類,同時新的類要實現被代理類實現的所有接口。

             (3)動態生成Java代碼,新加的業務邏輯方法由一定的邏輯代碼調用(在代碼中體現)。

             (4)編譯新生成的Java代碼.class文件。

             (5)重新加載到JVM中運行。

以上過程就叫字節碼重組。JDK中有一個規範,在ClassPath下只要是$開頭的.class文件,一般都是自動生成的。那麼珊門有沒有辦法看到代替後的對象的"真容"呢?做一個這樣測試,我們將內存中的對象字節碼通過文件流輸出到一個新的.class文件,然後^用反編譯工具查看其源代碼。

 

       CGLibJDK動態代理對比:

(1 ) JDK動態代理實現了被代理對象的接口,CGLib代理繼承了被代理對象

(2 )JDK動態代理和CGLib代理都在運行期生成字節碼JDK動態代理直接寫Class字節碼,CGLib 代理使用ASM框架寫Class字節碼,CGlib代理實現更復雜,生成代理類比JDK動態代理效率低

(3 )JDK動態代理調用代理方法是通過反射機制調用的,CGLib代理是通過FastClass機制直接調 用方法的,CGLib代理的執行效率更高

代理模式在Spring中的應用:

先看ProxyFactoryBean核心方法getObject(),源碼如下:

    @Nullable
    public Object getObject() throws BeansException {
        this.initializeAdvisorChain();
        if (this.isSingleton()) {
            return this.getSingletonInstance();
        } else {
            if (this.targetName == null) {
                this.logger.info("Using non-singleton proxies
                 with singleton targets is often undesirable. Enable prototype 
                  proxies by setting the 'targetName' property.");
            }

            return this.newPrototypeInstance();
        }
    }

getObjectO方法中,主要調用 getSingletonlnstance0newPrototypeInstanceO()。 Spring 的配置中如果不做任何設置,那麼Spring代理生成的Bean都是單例對象。如果修改scope,則每次 創建一個新的原型對象。newPrototypelnstanceQ裏面的邏輯比較複雜,在這不多加研究。

Spring利用動態代理實現AOP時有兩個非常重要的類:JdkDynamicAopProxy類和

CglibAopProxy來看一下類圖,如下圖所示。

.Spring中的代理選擇原則

(1 )當Bean有實現接口時,Spring就會用JDK動態代理。

(2 )Bean 沒有實現接口時,Spring CGLib 牌。

(3 ) Spring可以通過配置強制使用CGLib代理,只需在Spring的配置文件中加入如下代碼:

<aop: a spec tj-a utoproxy proxy-target-class="true"/>

 

靜態代理和動態代理的本質區別

(1)靜態態代理只能通過手動完成代理操作,如果被代理類增加了新的方法,代理類需要同步增加,

違背開閉原則。

(2)動態代理採用在運行時動態生成代碼的方式,取消了對被代理類的擴展限制,遵循開閉原則。

  (3) 若動態代理要對目標類的增強邏輯進行擴展,結合策略模式,只需要新增策略類便可完成,無

       須修改代理類的代碼。

 

代理模式的優缺點

代理模式具有以下優點:

(1 )代理模式能將代理對象與真實被調用目標對象分離。

(2 )在一定程度上降低了系統的耦合性,擴展性好。

(3 )可以起到保護目標對象的作用。

(4 )可以增強目標對象的功能。

當然,代理模式也有缺點:

(1 )代理模式會造成系統設計中類的數量增加。

(2 )在客戶端和目標對象中增加一個代理對象,會導致請求處理速度變慢。

(3 )增加了系統的複雜度。

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章