回顧一下基礎(chǔ)之靜態(tài)代理&動(dòng)態(tài)代理

項(xiàng)目中常用到代理模式,本篇文章帶大家回顧一下基本的操作以及動(dòng)態(tài)代理生成的類。

1. 定義

代理模式: 為其它對(duì)象提供代理,帶你對(duì)象挾持原對(duì)象類的引用,也稱委托模式。
作用:可以在不修改原對(duì)象的功能前提下,對(duì)原對(duì)象在功能進(jìn)行擴(kuò)展。(通俗講就是在當(dāng)你原對(duì)象封裝完畢或者你沒辦法修改,但是有一些增加的新的功能的時(shí)候,就可以在代理類上增加 符合“開閉原則”)

2. 代理的方式

  • 靜態(tài)代理

使用:手動(dòng)創(chuàng)建源代碼,在對(duì)其編譯。
缺點(diǎn):代理類跟原對(duì)象類實(shí)現(xiàn)一樣的接口,所以會(huì)有很多代理類。并且,當(dāng)接口增加方法,目標(biāo)對(duì)象與代理對(duì)象都要對(duì)應(yīng)修改。(怎么解決?改用動(dòng)態(tài)代理。)

  • 動(dòng)態(tài)代理

使用:在程序運(yùn)行時(shí),利用反射機(jī)制動(dòng)態(tài)創(chuàng)建。

3. 基本方式

  • 靜態(tài)代理的使用方式
    (下面以高低端電腦的例子展示一下基本使用方式)
    image.png
  1. 定義接口
/**
 * Create by ldr
 * on 2019/12/10 15:06.
 * 定義接口,電腦接口
 */
public interface Computer {
    void screen();//顯示器
    void memoryBank();//內(nèi)存條
    void mainBoard();//主板
    void graphicsCard();//顯卡
}
  1. 分別定義兩個(gè)被代理對(duì)象
------------------------------------------高配版電腦---------------------------------------------------
/**
 * Create by ldr
 * on 2019/12/10 15:08.
 * 高配電腦
 */
public class HeightConfigurationComputer implements Computer{
    private static final String TAG = "HeightComp";
    @Override
    public void screen() {
        System.out.println("高配置電腦 -- 屏幕");
    }

    @Override
    public void memoryBank() {
        System.out.println("高配置電腦 -- 內(nèi)存條");
    }

    @Override
    public void mainBoard() {
        System.out.println("高配置電腦 -- 主板");
    }

    @Override
    public void graphicsCard() {
        System.out.println("高配置電腦 -- 顯卡");
    }
}

------------------------------------------低配版電腦---------------------------------------------------
/**
 * Create by ldr
 * on 2019/12/10 15:08.
 * 低配電腦
 */
public class LowConfigurationComputer implements Computer {
    private static final String TAG = "LowComputer";
    @Override
    public void screen() {
        System.out.println("低配置電腦 -- 屏幕");
    }

    @Override
    public void memoryBank() {
        System.out.println("低配置電腦 -- 內(nèi)存條");
    }

    @Override
    public void mainBoard() {
        System.out.println("低配置電腦 -- 主板");
    }

    @Override
    public void graphicsCard() {
        System.out.println("低配置電腦 -- 顯卡");
    }
}
  1. 創(chuàng)建靜態(tài)代理類
/**
 * Create by ldr
 * on 2019/12/10 15:14.
 * 靜態(tài)代理
 */
public class ComputerStaticPro implements Computer {

    private Computer mComputer;

    public void setProx(Computer computer){
        mComputer = computer;
    }

    @Override
    public void screen() {
        System.out.println("代理類可先做共用一些邏輯~");
        mComputer.screen();
    }

    @Override
    public void memoryBank() {
        System.out.println("代理類可先做共用一些邏輯~");
        mComputer.memoryBank();
    }

    @Override
    public void mainBoard() {
        System.out.println("代理類可先做共用一些邏輯~");
        mComputer.mainBoard();
    }

    @Override
    public void graphicsCard() {
        System.out.println("代理類可先做共用一些邏輯~");
        mComputer.graphicsCard();
    }
}

  1. 使用方式
       HeightConfigurationComputer heightConfigurationComputer = new HeightConfigurationComputer();
        ComputerStaticPro staticPro = new ComputerStaticPro();
        staticPro.setProx(heightConfigurationComputer);
        staticPro.graphicsCard();
        staticPro.mainBoard();
        staticPro.screen();
        staticPro.memoryBank();

        //低配電腦
        staticPro.setProx(new LowConfigurationComputer());
        staticPro.graphicsCard();
        staticPro.mainBoard();
        staticPro.screen();
        staticPro.memoryBank();
-----------------------打印出來(lái)的Log
代理類可先做共用一些邏輯~
高配置電腦 -- 顯卡
代理類可先做共用一些邏輯~
高配置電腦 -- 主板
代理類可先做共用一些邏輯~
高配置電腦 -- 屏幕
代理類可先做共用一些邏輯~
高配置電腦 -- 內(nèi)存條
代理類可先做共用一些邏輯~
低配置電腦 -- 顯卡
代理類可先做共用一些邏輯~
低配置電腦 -- 主板
代理類可先做共用一些邏輯~
低配置電腦 -- 屏幕
代理類可先做共用一些邏輯~
低配置電腦 -- 內(nèi)存條
  • 動(dòng)態(tài)代理的使用方式
  1. 創(chuàng)建動(dòng)態(tài)代理類
/**
 * Create by ldr
 * on 2019/12/10 15:55.
 * 動(dòng)態(tài)代理
 */
public class ComputerDynamicPro implements InvocationHandler {
    //動(dòng)態(tài)代理類需要被代理的實(shí)例
    private Computer mComputer;

    public ComputerDynamicPro(Computer computer){
        mComputer = computer;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //關(guān)鍵點(diǎn)1
        //執(zhí)行被代理的方法的類  方法被調(diào)用時(shí)就會(huì)自動(dòng)調(diào)用
        Object invoke =  method.invoke(mComputer,args);
        //關(guān)鍵點(diǎn)2 
        String name = method.getName();
        if (name.equals("screen")){
            System.out.println(mComputer.getClass().getSimpleName()+"  screen()" );
        }else if (name.equals("memoryBank")){
            System.out.println(mComputer.getClass().getSimpleName()+"  memoryBank()" );
        }else if (name.equals("mainBoard")){
            System.out.println(mComputer.getClass().getSimpleName()+"  mainBoard()" );
        }else if(name.equals("graphicsCard")){
            System.out.println(mComputer.getClass().getSimpleName()+"  graphicsCard()" );
        }
        return invoke;
    }
}

動(dòng)態(tài)代理需要實(shí)現(xiàn)的是InvocationHandler接口,并重寫invoke方法,同樣也是要挾持被代理類的對(duì)象。
invoke方法中可以看到,使用的method.invoke(mComputer,args);傳入對(duì)象跟args便可以實(shí)現(xiàn)方法被調(diào)用時(shí)自動(dòng)調(diào)用。

在這個(gè)地方,其實(shí)你可以做的事情很多,比如你想知道方式的執(zhí)行時(shí)間,你可以在method.invoke()關(guān)鍵點(diǎn)1關(guān)鍵點(diǎn)2,加上時(shí)間,根據(jù)兩個(gè)時(shí)間差來(lái)計(jì)算一下這個(gè)方法的大致運(yùn)行時(shí)間。(實(shí)現(xiàn)AOP(面向程序切面編程))

  1. 使用
//關(guān)鍵代碼1
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//        //使用動(dòng)態(tài)代理
//        //高配電腦
HeightConfigurationComputer heightConfigurationComputer = new HeightConfigurationComputer(); 
ClassLoader classLoader = heightConfigurationComputer.getClass().getClassLoader();
Class[] interfaces = heightConfigurationComputer.getClass().getInterfaces();
ComputerDynamicPro computerDynamicPro = new ComputerDynamicPro(heightConfigurationComputer);

//通過(guò)Proxy.newProxyInstance生成代理類對(duì)象
// 關(guān)鍵代碼2
Object newProxyInstance = Proxy.newProxyInstance(classLoader,interfaces,computerDynamicPro);
Computer computer = (Computer) newProxyInstance;
computer.graphicsCard();
computer.mainBoard();
computer.memoryBank();
computer.screen();

關(guān)鍵代碼1:是看人家網(wǎng)上說(shuō)的可以將代理類生成的一句代碼,等下再貼一下代理類的代碼
關(guān)鍵代碼2:這里可以看到Proxy.newProxyInstance傳入了三個(gè)參數(shù)。

第一個(gè)參數(shù)ClassLoader loader:被代理的類的類加載器,通過(guò)它生成代理的class文件。

第二個(gè)參數(shù)Class<?>[] interfaces:被代理類實(shí)現(xiàn)的所有接口字節(jié)碼,代理類需要知道并實(shí)現(xiàn)

第三個(gè)參數(shù)InvocationHandler h:調(diào)用處理程序調(diào)度方法調(diào)用,也就是第一步我們定義好的那個(gè)ComputerDynamicPro 實(shí)現(xiàn)了InvocationHandler接口的動(dòng)態(tài)代理類。

我們來(lái)看一下關(guān)鍵代碼1中為我們生成的代理類的代碼吧


package com.sun.proxy;

import com.mzs.aptpro.Computer;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements Computer {
    private static Method m1;
    private static Method m5;
    private static Method m2;
    private static Method m3;
    private static Method m6;
    private static Method m0;
    private static Method m4;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void graphicsCard() throws  {
        try {
            super.h.invoke(this, m5, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void mainBoard() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void screen() throws  {
        try {
            super.h.invoke(this, m6, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void memoryBank() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m5 = Class.forName("com.mzs.aptpro.Computer").getMethod("graphicsCard");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.mzs.aptpro.Computer").getMethod("mainBoard");
            m6 = Class.forName("com.mzs.aptpro.Computer").getMethod("screen");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            m4 = Class.forName("com.mzs.aptpro.Computer").getMethod("memoryBank");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

可以看到上面class $Proxy0 extends Proxy implements Computer也是要實(shí)現(xiàn)我們的接口。在代碼最下面可以看到靜態(tài)代碼塊中,使用了反射將所有方法獲取到。
每個(gè)方法中都調(diào)用super.h.invoke()這樣的一個(gè)代碼 并且傳入三個(gè)參數(shù)this,反射獲取的方法,參數(shù)數(shù)組。
super.h實(shí)際上父類的h也是$Proxy0中的InvocationHandler,在構(gòu)造函數(shù)的時(shí)候傳遞上去的public $Proxy0(InvocationHandler var1) throws { super(var1); }
這里你就可以明白了為什么我們要?jiǎng)討B(tài)代理類要實(shí)現(xiàn)InvocationHandler并實(shí)現(xiàn)invoke方法,并且inovke方法的三個(gè)參數(shù)是什么時(shí)候傳遞進(jìn)來(lái)的。

總結(jié)一下:

靜態(tài)代理跟動(dòng)態(tài)代理的區(qū)別:
靜態(tài)代理:需要自己手動(dòng)生成類文件,并且被代理類跟代理類都需要實(shí)現(xiàn)同樣的接口,當(dāng)接口改變時(shí)代理類跟被代理類都需要修改。
動(dòng)態(tài)代理:只需實(shí)現(xiàn)InvocationHandler,并重寫invoke方法。不需要知道也用不著實(shí)現(xiàn)接口,當(dāng)接口改變的時(shí)候也只需要改變invoke中的邏輯便可以。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容