Carson帶你學(xué)設(shè)計(jì)模式:動(dòng)態(tài)代理模式(Proxy Pattern)

前言

今天Carson來(lái)全面總結(jié)最常用的設(shè)計(jì)模式 - 代理模式中的動(dòng)態(tài)代理模式

其他設(shè)計(jì)模式介紹
這是一份全面 & 詳細(xì)的設(shè)計(jì)模式學(xué)習(xí)指南
Carson帶你學(xué)設(shè)計(jì)模式:?jiǎn)卫J剑⊿ingleton)
Carson帶你學(xué)設(shè)計(jì)模式:簡(jiǎn)單工廠模式(SimpleFactoryPattern)
Carson帶你學(xué)設(shè)計(jì)模式:工廠方法模式(Factory Method)
Carson帶你學(xué)設(shè)計(jì)模式:抽象工廠模式(Abstract Factory)
Carson帶你學(xué)設(shè)計(jì)模式:策略模式(Strategy Pattern)
Carson帶你學(xué)設(shè)計(jì)模式:適配器模式(Adapter Pattern)
Carson帶你學(xué)設(shè)計(jì)模式:靜態(tài)代理模式(Proxy Pattern)
Carson帶你學(xué)設(shè)計(jì)模式:動(dòng)態(tài)代理模式(Proxy Pattern)
Carson帶你學(xué)設(shè)計(jì)模式:模板方法模式(Template Method)
Carson帶你學(xué)設(shè)計(jì)模式:建造者模式(Builder Pattern)
Carson帶你學(xué)設(shè)計(jì)模式:外觀模式(Facade Pattern)
Carson帶你學(xué)設(shè)計(jì)模式:觀察者模式(Observer)


目錄

示意圖

1. 為什么要使用動(dòng)態(tài)代理

1.1 背景

代理模式中的靜態(tài)代理模式存在一些特點(diǎn):

  • 1個(gè)靜態(tài)代理 只服務(wù)1種類型的目標(biāo)對(duì)象
  • 若要服務(wù)多類型的目標(biāo)對(duì)象篡悟,則需要為每種目標(biāo)對(duì)象都實(shí)現(xiàn)一個(gè)靜態(tài)代理對(duì)象

關(guān)于靜態(tài)代理模式可以看文章:Carson帶你學(xué)設(shè)計(jì)模式:靜態(tài)代理模式(Proxy Pattern)

1.2 沖突

在目標(biāo)對(duì)象較多的情況下焙矛,若采用靜態(tài)代理,則會(huì)出現(xiàn) 靜態(tài)代理對(duì)象量多羡宙、代碼量大凌盯,從而導(dǎo)致代碼復(fù)雜的問題

1.3 解決方案

采用 動(dòng)態(tài)代理模式孕豹。


2. 動(dòng)態(tài)代理模式介紹

2.1 實(shí)現(xiàn)原理

  • 設(shè)計(jì)動(dòng)態(tài)代理類(DynamicProxy)時(shí),不需要顯式實(shí)現(xiàn)與目標(biāo)對(duì)象類(RealSubject)相同的接口十气,而是將這種實(shí)現(xiàn)推遲到程序運(yùn)行時(shí)由 JVM來(lái)實(shí)現(xiàn)
  1. 即:在使用時(shí)再創(chuàng)建動(dòng)態(tài)代理類 & 實(shí)例
  2. 靜態(tài)代理則是在代理類實(shí)現(xiàn)時(shí)就指定與目標(biāo)對(duì)象類(RealSubject)相同的接口
  • 通過Java 反射機(jī)制的method.invoke()励背,通過調(diào)用動(dòng)態(tài)代理類對(duì)象方法,從而自動(dòng)調(diào)用目標(biāo)對(duì)象的方法

2.2 優(yōu)點(diǎn)

  • 只需要1個(gè)動(dòng)態(tài)代理類就可以解決創(chuàng)建多個(gè)靜態(tài)代理的問題砸西,避免重復(fù)叶眉、多余代碼
  • 更強(qiáng)的靈活性
  1. 設(shè)計(jì)動(dòng)態(tài)代理類(DynamicProxy)時(shí),不需要顯式實(shí)現(xiàn)與目標(biāo)對(duì)象類(RealSubject)相同的接口芹枷,而是將這種實(shí)現(xiàn)推遲到程序運(yùn)行時(shí)由 JVM來(lái)實(shí)現(xiàn)
  2. 在使用時(shí)(調(diào)用目標(biāo)對(duì)象方法時(shí))才會(huì)動(dòng)態(tài)創(chuàng)建動(dòng)態(tài)代理類 & 實(shí)例衅疙,不需要事先實(shí)例化

2.3 缺點(diǎn)

  • 效率低
    相比靜態(tài)代理中 直接調(diào)用目標(biāo)對(duì)象方法,動(dòng)態(tài)代理則需要先通過Java反射機(jī)制 從而 間接調(diào)用目標(biāo)對(duì)象方法
  • 應(yīng)用場(chǎng)景局限
    因?yàn)?Java 的單繼承特性(每個(gè)代理類都繼承了 Proxy 類)鸳慈,即只能針對(duì)接口 創(chuàng)建 代理類饱溢,不能針對(duì)類 創(chuàng)建代理類

即只能動(dòng)態(tài)代理 實(shí)現(xiàn)了接口的類

2.4 應(yīng)用場(chǎng)景

  • 基于靜態(tài)代理應(yīng)用場(chǎng)景下,需要代理對(duì)象數(shù)量較多的情況下使用動(dòng)態(tài)代理
  • AOP 領(lǐng)域
  1. 定義:即 Aspect Oriented Programming = 面向切面編程走芋,是OOP的延續(xù)绩郎、函數(shù)式編程的一種衍生范型
  2. 作用:通過預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)。
  3. 優(yōu)點(diǎn):降低業(yè)務(wù)邏輯各部分之間的耦合度 翁逞、 提高程序的可重用性 & 提高了開發(fā)的效率
  4. 具體應(yīng)用場(chǎng)景:日志記錄肋杖、性能統(tǒng)計(jì)、安全控制挖函、異常處理等

2.5 與靜態(tài)代理模式的區(qū)別

示意圖

3. 具體應(yīng)用

接下來(lái)状植,我將用1個(gè)具體實(shí)例來(lái)對(duì) 動(dòng)態(tài)代理模式 進(jìn)行更深一步的介紹

3.1 實(shí)例概況

  • 背景:小成 希望買一臺(tái)最新的頂配 Mac 電腦;小何希望買一臺(tái) iPhone
  • 沖突:國(guó)內(nèi)還沒上,只有美國(guó)才有
  • 解決方案:尋找一個(gè)代購(gòu)一起進(jìn)行購(gòu)買
  1. 即1個(gè)代購(gòu)(動(dòng)態(tài)代理對(duì)象)同時(shí) 代替 小成 & 小何(目標(biāo)對(duì)象) 去買Mac(間接訪問的操作)
  2. 該代購(gòu)是代購(gòu)任何商品 = 什么人有什么需求就會(huì)去代購(gòu)任何東西(動(dòng)態(tài)代理)

3.2 使用步驟

  1. 聲明 調(diào)用處理器類
  2. 聲明目標(biāo)對(duì)象類的抽象接口
  3. 聲明目標(biāo)對(duì)象類
  4. 通過動(dòng)態(tài)代理對(duì)象津畸,調(diào)用目標(biāo)對(duì)象的方法

3.3 步驟詳解

步驟1: 聲明 調(diào)用處理器類

DynamicProxy.java

<-- 作用 -->
// 1.  生成 動(dòng)態(tài)代理對(duì)象
// 2.  指定 代理對(duì)象運(yùn)行目標(biāo)對(duì)象方法時(shí)需要完成的 具體任務(wù)
// 注:需實(shí)現(xiàn)InvocationHandler接口 = 調(diào)用處理器 接口
// 所以稱為 調(diào)用處理器類

 public class DynamicProxy implements InvocationHandler {

    // 聲明代理對(duì)象
    // 作用:綁定關(guān)系振定,即關(guān)聯(lián)到哪個(gè)接口(與具體的實(shí)現(xiàn)類綁定)的哪些方法將被調(diào)用時(shí),執(zhí)行invoke()
    private Object ProxyObject;

    public Object newProxyInstance(Object ProxyObject){
        this.ProxyObject =ProxyObject;
        return Proxy.newProxyInstance(ProxyObject.getClass().getClassLoader(),
                ProxyObject.getClass().getInterfaces(),this);
        // Proxy類 = 動(dòng)態(tài)代理類的主類 
        // Proxy.newProxyInstance()作用:根據(jù)指定的類裝載器肉拓、一組接口 & 調(diào)用處理器 生成動(dòng)態(tài)代理類實(shí)例后频,并最終返回
        // 參數(shù)說(shuō)明:
        // 參數(shù)1:指定產(chǎn)生代理對(duì)象的類加載器,需要將其指定為和目標(biāo)對(duì)象同一個(gè)類加載器
        // 參數(shù)2:指定目標(biāo)對(duì)象的實(shí)現(xiàn)接口
        // 即要給目標(biāo)對(duì)象提供一組什么接口帝簇。若提供了一組接口給它徘郭,那么該代理對(duì)象就默認(rèn)實(shí)現(xiàn)了該接口,這樣就能調(diào)用這組接口中的方法
        // 參數(shù)3:指定InvocationHandler對(duì)象丧肴。即動(dòng)態(tài)代理對(duì)象在調(diào)用方法時(shí)残揉,會(huì)關(guān)聯(lián)到哪個(gè)InvocationHandler對(duì)象

    }

    //  復(fù)寫InvocationHandler接口的invoke()
    //  動(dòng)態(tài)代理對(duì)象調(diào)用目標(biāo)對(duì)象的任何方法前,都會(huì)調(diào)用調(diào)用處理器類的invoke()
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            // 參數(shù)說(shuō)明:
            // 參數(shù)1:動(dòng)態(tài)代理對(duì)象(即哪個(gè)動(dòng)態(tài)代理對(duì)象調(diào)用了method()
            // 參數(shù)2:目標(biāo)對(duì)象被調(diào)用的方法
            // 參數(shù)3:指定被調(diào)用方法的參數(shù)
            throws Throwable {
                System.out.println("代購(gòu)出門了");
                Object result = null;
                // 通過Java反射機(jī)制調(diào)用目標(biāo)對(duì)象方法
                result = method.invoke(ProxyObject, args);
        return result;
    }

}


步驟2: 聲明目標(biāo)對(duì)象的抽象接口

Subject.java

public interface Subject {
    // 定義目標(biāo)對(duì)象的接口方法
    // 代購(gòu)物品
    public  void buybuybuy();

}

步驟3: 聲明目標(biāo)對(duì)象類

Buyer1.java


// 小成芋浮,真正的想買Mac的對(duì)象 = 目標(biāo)對(duì)象 = 被代理的對(duì)象
// 實(shí)現(xiàn)抽象目標(biāo)對(duì)象的接口
public class Buyer1 implements Subject  {

    @Override
    public void buybuybuy() {
        System.out.println("小成要買Mac");
    }

}

Buyer2.java

// 小何抱环,真正的想買iPhone的對(duì)象 = 目標(biāo)對(duì)象 = 被代理的對(duì)象
// 實(shí)現(xiàn)抽象目標(biāo)對(duì)象的接口
public class Buyer2 implements Subject  {

    @Override
    public void buybuybuy() {
        System.out.println("小何要買iPhone");
    }

}

步驟4: 通過動(dòng)態(tài)代理對(duì)象,調(diào)用目標(biāo)對(duì)象的方法
MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 1. 創(chuàng)建調(diào)用處理器類對(duì)象
        DynamicProxy DynamicProxy = new DynamicProxy();

        // 2. 創(chuàng)建目標(biāo)對(duì)象對(duì)象
        Buyer1 mBuyer1 = new Buyer1();

        // 3. 創(chuàng)建動(dòng)態(tài)代理類 & 對(duì)象:通過調(diào)用處理器類對(duì)象newProxyInstance()
        // 傳入上述目標(biāo)對(duì)象對(duì)象
        Subject Buyer1_DynamicProxy = (Subject) DynamicProxy.newProxyInstance(mBuyer1);

        // 4. 通過調(diào)用動(dòng)態(tài)代理對(duì)象方法從而調(diào)用目標(biāo)對(duì)象方法
        // 實(shí)際上是調(diào)用了invoke()纸巷,再通過invoke()里的反射機(jī)制調(diào)用目標(biāo)對(duì)象的方法
        Buyer1_DynamicProxy.buybuybuy();
        // 以上代購(gòu)為小成代購(gòu)Mac

        // 以下是代購(gòu)為小何代購(gòu)iPhone
        Buyer2 mBuyer2 = new Buyer2();
        Subject Buyer2_DynamicProxy = (Subject) DynamicProxy.newProxyInstance(mBuyer2);
        Buyer2_DynamicProxy.buybuybuy();
    }
}

3.4 測(cè)試結(jié)果

示意圖

3.5 Demo地址

Carson_Ho的Github地址:動(dòng)態(tài)代理DynamicProxy


4. 源碼分析

  • 在經(jīng)過上面的實(shí)例后镇草,你是否會(huì)對(duì)以下問題好奇:

    1. 動(dòng)態(tài)代理類 及其對(duì)象實(shí)例是如何生成的?
    2. 如何通過調(diào)用動(dòng)態(tài)代理對(duì)象方法瘤旨,從而調(diào)用目標(biāo)對(duì)象方法梯啤?
  • 下面,我們順著 步驟4:目標(biāo)對(duì)象 通過 動(dòng)態(tài)代理對(duì)象調(diào)用方法的使用 來(lái)進(jìn)行動(dòng)態(tài)代理模式的源碼分析

// 步驟4:通過動(dòng)態(tài)代理對(duì)象存哲,調(diào)用目標(biāo)對(duì)象的方法
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 1. 創(chuàng)建 調(diào)用處理器類 對(duì)象
        DynamicProxy DynamicProxy = new DynamicProxy();

        // 2. 創(chuàng)建 目標(biāo)類 對(duì)象
        Buyer1 mBuyer1 = new Buyer1();

        // 3. 創(chuàng)建動(dòng)態(tài)代理類 & 對(duì)象:通過調(diào)用處理器類對(duì)象newProxyInstance()
        // 傳入上述目標(biāo)類對(duì)象
        Subject Buyer1_DynamicProxy = (Subject) DynamicProxy.newProxyInstance(mBuyer1);
        // ->>關(guān)注1

        // 4. 通過調(diào)用動(dòng)態(tài)代理對(duì)象方法從而調(diào)用目標(biāo)對(duì)象方法
        // ->>關(guān)注2
        Buyer1_DynamicProxy.buybuybuy();
        // 以上代購(gòu)為小成代購(gòu)Mac

        // 以下是代購(gòu)為小何代購(gòu)iPhone
        Buyer2 mBuyer2 = new Buyer2();
        Subject Buyer2_DynamicProxy = (Subject) DynamicProxy.newProxyInstance(mBuyer2);
        Buyer2_DynamicProxy.buybuybuy();
    }
}

此處有兩個(gè)重要的源碼分析點(diǎn):

  • 關(guān)注1:創(chuàng)建動(dòng)態(tài)代理類 & 對(duì)象:通過調(diào)用處理器類對(duì)象newProxyInstance()

解決的問題是:動(dòng)態(tài)代理類 及其對(duì)象實(shí)例是如何生成的因宇?

  • 關(guān)注2:通過調(diào)用動(dòng)態(tài)代理對(duì)象方法從而調(diào)用目標(biāo)對(duì)象方法

解決的問題是:如何通過調(diào)用動(dòng)態(tài)代理對(duì)象方法,從而調(diào)用目標(biāo)對(duì)象方法祟偷?

下面察滑,我們將主要分析這兩處源碼。

4.1 (關(guān)注1)創(chuàng)建動(dòng)態(tài)代理類 & 對(duì)象:通過調(diào)用處理器類對(duì)象newProxyInstance()

// 使用代碼
Subject Buyer1_DynamicProxy = (Subject) DynamicProxy.newProxyInstance(mBuyer1);
  • 即修肠,動(dòng)態(tài)代理類 及其對(duì)象實(shí)例是如何生成的贺辰?
  • 下面,我們直接進(jìn)入DynamicProxy.newProxyInstance()
<-- 關(guān)注1:調(diào)用處理器 類的newProxyInstance() -->
// 即步驟1中實(shí)現(xiàn)的類:DynamicProxy

// 作用:
// 1.  生成 動(dòng)態(tài)代理對(duì)象
// 2.  指定 代理對(duì)象運(yùn)行目標(biāo)對(duì)象方法時(shí)需要完成的 具體任務(wù)
// 注:需實(shí)現(xiàn)InvocationHandler接口 = 調(diào)用處理器 接口

 public class DynamicProxy implements InvocationHandler {

    // 聲明代理對(duì)象
    private Object ProxyObject;

    public Object newProxyInstance(Object ProxyObject){
        this.ProxyObject =ProxyObject;
        return Proxy.newProxyInstance(ProxyObject.getClass().getClassLoader(),
                ProxyObject.getClass().getInterfaces(),this);
        // Proxy.newProxyInstance()作用:根據(jù)指定的類裝載器嵌施、一組接口 & 調(diào)用處理器 生成動(dòng)態(tài)代理類實(shí)例饲化,并最終返回
        // ->>關(guān)注2

    }
    
     // 以下暫時(shí)忽略,下文會(huì)詳細(xì)介紹
    //  復(fù)寫InvocationHandler接口的invoke()
    //  動(dòng)態(tài)代理對(duì)象調(diào)用目標(biāo)對(duì)象的任何方法前艰管,都會(huì)調(diào)用調(diào)用處理器類的invoke()
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            // 參數(shù)說(shuō)明:
            // 參數(shù)1:動(dòng)態(tài)代理對(duì)象(即哪個(gè)動(dòng)態(tài)代理對(duì)象調(diào)用了method()
            // 參數(shù)2:目標(biāo)對(duì)象被調(diào)用的方法
            // 參數(shù)3:指定被調(diào)用方法的參數(shù)
            throws Throwable {
                System.out.println("代購(gòu)出門了");
                Object result = null;
                // 通過Java反射機(jī)制調(diào)用目標(biāo)對(duì)象方法
                result = method.invoke(ProxyObject, args);
        return result;
    }

// 至此滓侍,關(guān)注1分析完畢,跳出
}

<-- 關(guān)注2:newProxyInstance()源碼解析-->
// 作用:根據(jù)指定的類裝載器牲芋、一組接口 & 調(diào)用處理器 生成動(dòng)態(tài)代理類及其對(duì)象實(shí)例,并最終返回
      
public static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h) throws IllegalArgumentException { 
  // 參數(shù)說(shuō)明:
        // 參數(shù)1:指定產(chǎn)生代理對(duì)象的類加載器,需要將其指定為和目標(biāo)對(duì)象同一個(gè)類加載器
        // 參數(shù)2:指定目標(biāo)對(duì)象的實(shí)現(xiàn)接口
        // 即要給目標(biāo)對(duì)象提供一組什么接口缸浦。若提供了一組接口給它夕冲,那么該代理對(duì)象就默認(rèn)實(shí)現(xiàn)了該接口,這樣就能調(diào)用這組接口中的方法
        // 參數(shù)3:指定InvocationHandler對(duì)象裂逐。即動(dòng)態(tài)代理對(duì)象在調(diào)用方法時(shí)歹鱼,會(huì)關(guān)聯(lián)到哪個(gè)InvocationHandler對(duì)象

    ...  
    // 僅貼出核心代碼

    // 1. 通過 為Proxy類指定類加載器對(duì)象 & 一組interface,從而創(chuàng)建動(dòng)態(tài)代理類
    // >>關(guān)注3
    Class cl = getProxyClass(loader, interfaces); 

    // 2. 通過反射機(jī)制獲取動(dòng)態(tài)代理類的構(gòu)造函數(shù)卜高,其參數(shù)類型是調(diào)用處理器接口類型
    Constructor cons = cl.getConstructor(constructorParams); 

    // 3. 通過動(dòng)態(tài)代理類的構(gòu)造函數(shù) 創(chuàng)建 代理類實(shí)例(傳入調(diào)用處理器對(duì)象
    return (Object) cons.newInstance(new Object[] { h }); 

// 特別注意 
// 1. 動(dòng)態(tài)代理類繼承 Proxy 類 & 并實(shí)現(xiàn)了在Proxy.newProxyInstance()中提供的一系列接口(接口數(shù)組)
// 2. Proxy 類中有一個(gè)映射表
  // 映射關(guān)系為:(<ClassLoader>,(<Interfaces>,<ProxyClass>) )
  // 即:1級(jí)key = 類加載器弥姻,根據(jù)1級(jí)key 得到 2級(jí)key = 接口數(shù)組
  // 因此:1類加載器對(duì)象 + 1接口數(shù)組 = 確定了一個(gè)代理類實(shí)例
...

// 回到調(diào)用關(guān)注2的原處
}


<-- 關(guān)注3:getProxyClass()源碼分析 -->
// 作用:創(chuàng)建動(dòng)態(tài)代理類

public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces)  
throws IllegalArgumentException { 
    
  ...
  // 僅貼出關(guān)鍵代碼
  
<-- 1. 將目標(biāo)類所實(shí)現(xiàn)的接口加載到內(nèi)存中 -->
    // 遍歷目標(biāo)類所實(shí)現(xiàn)的接口  
    for (int i = 0; i < interfaces.length; i++) {  
          
        // 獲取目標(biāo)類實(shí)現(xiàn)的接口名稱 
        String interfaceName = interfaces[i].getName();  
        Class interfaceClass = null;  
        try {  
        // 加載目標(biāo)類實(shí)現(xiàn)的接口到內(nèi)存中  
        interfaceClass = Class.forName(interfaceName, false, loader);  
        } catch (ClassNotFoundException e) {  
        }  
        if (interfaceClass != interfaces[i]) {  
        throw new IllegalArgumentException(  
            interfaces[i] + " is not visible from class loader");  
        }  
    }  
       
<-- 2. 生成動(dòng)態(tài)代理類 -->       
        // 根據(jù)傳入的接口 & 代理對(duì)象 創(chuàng)建動(dòng)態(tài)代理類的字節(jié)碼
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(  
            proxyName, interfaces);  

            // 根據(jù)動(dòng)態(tài)代理類的字節(jié)碼 生成 動(dòng)態(tài)代理類
            proxyClass = defineClass0(loader, proxyName,  
            proxyClassFile, 0, proxyClassFile.length);  
       
        }  
    // 最終返回動(dòng)態(tài)代理類
    return proxyClass;  
    }  

// 回到調(diào)用關(guān)注3的原處

總結(jié)

  • 通過調(diào)用處理器類對(duì)象的.newProxyInstance()創(chuàng)建動(dòng)態(tài)代理類 及其實(shí)例對(duì)象(需傳入目標(biāo)類對(duì)象)
  • 具體過程如下:
    1. 通過 為Proy類指定類加載器對(duì)象 & 一組接口,從而創(chuàng)建動(dòng)態(tài)代理類的字節(jié)碼掺涛;再根據(jù)類字節(jié)碼創(chuàng)建動(dòng)態(tài)代理類
    2. 通過反射機(jī)制獲取動(dòng)態(tài)代理類的構(gòu)造函數(shù)(參數(shù)類型 = 調(diào)用處理器接口類型
    3. 通過動(dòng)態(tài)代理類的構(gòu)造函數(shù) 創(chuàng)建 代理類實(shí)例(傳入調(diào)用處理器對(duì)象

4.2 (關(guān)注2)通過調(diào)用動(dòng)態(tài)代理對(duì)象方法從而調(diào)用目標(biāo)對(duì)象方法

即庭敦,如何通過調(diào)用動(dòng)態(tài)代理對(duì)象方法,從而調(diào)用目標(biāo)對(duì)象方法薪缆?

// 使用代碼
Buyer1_DynamicProxy.buybuybuy();
  • 在關(guān)注1中的 DynamicProxy.newProxyInstance()生成了一個(gè)動(dòng)態(tài)代理類 及其實(shí)例

該動(dòng)態(tài)代理類記為 :$Proxy0

下面我們直接看該類實(shí)現(xiàn)及其 buybuybuy()

  • 該方法的邏輯如下:
<-- 動(dòng)態(tài)代理類 $Proxy0 實(shí)現(xiàn)-->
// 繼承:Java 動(dòng)態(tài)代理機(jī)制的主類:java.lang.reflect.Proxy
// 實(shí)現(xiàn):與目標(biāo)對(duì)象一樣的接口(即上文例子的Subject接口)
public final class $Proxy0 extends Proxy implements Subject {

// 構(gòu)造函數(shù)
public ProxySubject(InvocationHandler invocationhandler)   
    {   
        super(invocationhandler);   
    }  

 // buybuybuy()是目標(biāo)對(duì)象實(shí)現(xiàn)接口(Subject)中的方法
 // 即$Proxy0類必須實(shí)現(xiàn)
 // 所以在使用動(dòng)態(tài)代理類對(duì)象時(shí)秧廉,才可以調(diào)用目標(biāo)對(duì)象的同名方法(即上文的buybuybuy())
 public final void buybuybuy() {
        try {
            super.h.invoke(this, m3, null); 
            // 該方法的邏輯實(shí)際上是調(diào)用了父類Proxy類的h參數(shù)的invoke()
            // h參數(shù)即在Proxy.newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h)傳入的第3個(gè)參數(shù)InvocationHandler對(duì)象
            // 即調(diào)用了調(diào)用處理器的InvocationHandler.invoke()
            // 而復(fù)寫的invoke()利用反射機(jī)制:Object result=method.invoke(proxied,args)
            // 從而調(diào)用目標(biāo)對(duì)象的的方法 ->>關(guān)注4
            return;
        } catch (Error e) {
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

<-- 關(guān)注4:調(diào)用處理器 類復(fù)寫的invoke() -->
// 即步驟1中實(shí)現(xiàn)的類:DynamicProxy
// 內(nèi)容很多都分析過了,直接跳到復(fù)寫的invoke()中

 public class DynamicProxy implements InvocationHandler {

    private Object ProxyObject;

    public Object newProxyInstance(Object ProxyObject){
        this.ProxyObject =ProxyObject;
        return Proxy.newProxyInstance(ProxyObject.getClass().getClassLoader(),
                ProxyObject.getClass().getInterfaces(),this);
     

    }

    //  復(fù)寫InvocationHandler接口的invoke()
    //  動(dòng)態(tài)代理對(duì)象調(diào)用目標(biāo)對(duì)象的任何方法前拣帽,都會(huì)調(diào)用調(diào)用處理器類的invoke()
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            // 參數(shù)說(shuō)明:
            // 參數(shù)1:動(dòng)態(tài)代理對(duì)象(即哪個(gè)動(dòng)態(tài)代理對(duì)象調(diào)用了method()
            // 參數(shù)2:目標(biāo)對(duì)象被調(diào)用的方法
            // 參數(shù)3:指定被調(diào)用方法的參數(shù)
            throws Throwable {
                System.out.println("代購(gòu)出門了");
                Object result = null;
                // 通過Java反射機(jī)制調(diào)用目標(biāo)對(duì)象方法
                result = method.invoke(ProxyObject, args);
        return result;
    }

總結(jié)

  • 動(dòng)態(tài)代理類實(shí)現(xiàn)了與目標(biāo)類一樣的接口疼电,并實(shí)現(xiàn)了需要目標(biāo)類對(duì)象需要調(diào)用的方法
  • 該方法的實(shí)現(xiàn)邏輯 = 調(diào)用父類 Proxy類的 h.invoke()

其中h參數(shù) = 在創(chuàng)建動(dòng)態(tài)代理實(shí)例中newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h)傳入的第3個(gè)參數(shù)InvocationHandler對(duì)象

  • InvocationHandler.invoke() 中通過反射機(jī)制,從而調(diào)用目標(biāo)類對(duì)象的方法

4.3 原理總結(jié)

我用一張圖總結(jié)第4節(jié)說(shuō)的關(guān)于動(dòng)態(tài)代理模式的源碼分析减拭。


示意圖

至此蔽豺,關(guān)于代理模式中的動(dòng)態(tài)代理模式的相關(guān)知識(shí)已經(jīng)講解完畢。


5. 總結(jié)

我用兩張圖總結(jié)整篇文章的內(nèi)容

示意圖
示意圖2
  • 本文主要對(duì)動(dòng)態(tài)代理模式進(jìn)行了全面介紹
  • 接下來(lái)我會(huì)對(duì)每種設(shè)計(jì)模式進(jìn)行詳細(xì)的分析拧粪,歡迎關(guān)注Carson_Ho的簡(jiǎn)書修陡,不定期分享關(guān)于安卓開發(fā)的干貨,追求短既们、平濒析、快,但卻不缺深度啥纸。

請(qǐng)點(diǎn)贊号杏!因?yàn)槟愕墓膭?lì)是我寫作的最大動(dòng)力!

相關(guān)文章閱讀
這是一份全面 & 詳細(xì)的設(shè)計(jì)模式學(xué)習(xí)指南
Carson帶你學(xué)設(shè)計(jì)模式:?jiǎn)卫J剑⊿ingleton)
Carson帶你學(xué)設(shè)計(jì)模式:簡(jiǎn)單工廠模式(SimpleFactoryPattern)
Carson帶你學(xué)設(shè)計(jì)模式:工廠方法模式(Factory Method)
Carson帶你學(xué)設(shè)計(jì)模式:抽象工廠模式(Abstract Factory)
Carson帶你學(xué)設(shè)計(jì)模式:策略模式(Strategy Pattern)
Carson帶你學(xué)設(shè)計(jì)模式:適配器模式(Adapter Pattern)
Carson帶你學(xué)設(shè)計(jì)模式:靜態(tài)代理模式(Proxy Pattern)
Carson帶你學(xué)設(shè)計(jì)模式:動(dòng)態(tài)代理模式(Proxy Pattern)
Carson帶你學(xué)設(shè)計(jì)模式:模板方法模式(Template Method)
Carson帶你學(xué)設(shè)計(jì)模式:建造者模式(Builder Pattern)
Carson帶你學(xué)設(shè)計(jì)模式:外觀模式(Facade Pattern)
Carson帶你學(xué)設(shè)計(jì)模式:觀察者模式(Observer)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末斯棒,一起剝皮案震驚了整個(gè)濱河市盾致,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌荣暮,老刑警劉巖庭惜,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異穗酥,居然都是意外死亡护赊,警方通過查閱死者的電腦和手機(jī)惠遏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)骏啰,“玉大人节吮,你說(shuō)我怎么就攤上這事∨懈” “怎么了透绩?”我有些...
    開封第一講書人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)壁熄。 經(jīng)常有香客問我帚豪,道長(zhǎng),這世上最難降的妖魔是什么草丧? 我笑而不...
    開封第一講書人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任狸臣,我火速辦了婚禮,結(jié)果婚禮上方仿,老公的妹妹穿的比我還像新娘固棚。我一直安慰自己,他們只是感情好仙蚜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開白布此洲。 她就那樣靜靜地躺著,像睡著了一般委粉。 火紅的嫁衣襯著肌膚如雪呜师。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,718評(píng)論 1 305
  • 那天贾节,我揣著相機(jī)與錄音汁汗,去河邊找鬼。 笑死栗涂,一個(gè)胖子當(dāng)著我的面吹牛知牌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播斤程,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼角寸,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了忿墅?” 一聲冷哼從身側(cè)響起扁藕,我...
    開封第一講書人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎疚脐,沒想到半個(gè)月后亿柑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡棍弄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年望薄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了疟游。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡式矫,死狀恐怖乡摹,靈堂內(nèi)的尸體忽然破棺而出役耕,到底是詐尸還是另有隱情采转,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布瞬痘,位于F島的核電站故慈,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏框全。R本人自食惡果不足惜察绷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望津辩。 院中可真熱鬧拆撼,春花似錦、人聲如沸喘沿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蚜印。三九已至莺禁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間窄赋,已是汗流浹背哟冬。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留忆绰,地道東北人浩峡。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像错敢,于是被迫代替她去往敵國(guó)和親翰灾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容