抽絲剝繭——代理設計模式

代理設計模式

代理設計模式再生活中應該很常見了,如今各類中間商的貨物代售方便了咱們的生活也增長了咱們生活的成本。這種生活中的中間商行爲就是一種代理模式。java

拿一個品牌來講明:編程

在編程領域中通常存在兩種代理模式設計模式

  • 靜態代理。(僅僅能夠代理一個類的行爲,不能隨類的變化而變化)
  • 動態代理。(能夠代理全部類的行爲)

接下來咱們先來看靜態代理ide

1. 靜態代理

僅僅用來代理一個類的行爲。函數

代碼演示一下:工具

  • 繼承實現代理(不推薦,耦合性大
class NaiKe {

    void run() {
        System.out.println("耐克");
    }
}


//代理類
class ShoesProxy extends NaiKe{
    @Override
    void run() {
        System.out.println("agency shoes before");
        super.run();
        System.out.println("agency shoes after");
    }
}
  • 組合實現代理(推薦)
class NaiKe{

    void run() {
        System.out.println("耐克");
    }
}

class ShoesProxy {

    NaiKe naiKe = new NaiKe();

    void run() {
        System.out.println("agency shoes before");
        naiKe.run();
        System.out.println("agency shoes after");
    }
}
  • 多態實現代理,多個代理嵌套
public class ProxyDesgin {
    public static void main(String[] args) {
        Shoes shoes = new ShoesProxy(new ShoesTimer(new NaiKe()));
        shoes.run();
    }
}

abstract class Shoes{
   abstract void run();
}

class NaiKe extends Shoes{

    @Override
    void run() {
        System.out.println("耐克");
    }
}

class Adi extends Shoes{
    @Override
    void run() {
        System.out.println("阿迪達斯");
    }
}

//代理類
class ShoesProxy extends Shoes {

    Shoes shoes ;

    public ShoesProxy(Shoes shoes){
        this.shoes = shoes ;
    }

    void run() {
        System.out.println("agency shoes before");
        shoes.run();
        System.out.println("agency shoes after");
    }
}


class ShoesTimer extends Shoes {

    Shoes shoes ;

    public ShoesTimer(Shoes shoes){
        this.shoes = shoes ;
    }

    void run() {
        System.out.println("log timer shoes before");
        shoes.run();
        System.out.println("log timer shoes after");
    }
}

畫個圖瞅瞅靜態代理this

這個就是靜態代理,兄弟們應該已經發現了它的缺點,只能指定本身想要進行代理的類,而不能對全部的類進行代理,擴展性太差,因此引出了動態代理設計

2.動態代理

談到動態代理,腦子裏第一個出現的確定就是Java動態代理了。咱們先來聊一下Java動態代理。代理

2.1 Java動態代理

先來看一個動態代理的案例code

NaiKe naiKe = new NaiKe();
        Shoes shoes = (Shoes) Proxy.newProxyInstance(NaiKe.class.getClassLoader(), new Class[]{Shoes.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("begin timer : " + System.currentTimeMillis());
                method.invoke(naiKe,args);
                System.out.println("after timer : " + System.currentTimeMillis());
                return null;
            }
        });
        shoes.run();
  • 第一個參數。經過動態代理建立的對象被哪一個加載器加載,通常使用本類的類加載器便可
  • 第二個參數。被代理對象要實現的方法
  • 第三個參數。被代理對象被調用的時候該如何處理邏輯

咱們看一下動態代理的源碼。

咱們能夠經過如下方式讓JVM將動態生成的代理類保存到咱們的項目中

  • JDK1.8使用System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
  • JDK1.8以上能夠使用1 System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");

生成的代理類以下:

final class $Proxy0 extends Proxy implements Shoes {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

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

    public final boolean equals(Object var1) throws  {
    }

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

    public final String toString() throws  {
    }

    public final int hashCode() throws  {
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("desgin.proxy.Shoes").getMethod("run");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

從這個類的結構中,咱們能夠看出不少的東西

  • 爲何說JAVA動態代理僅僅只能代理接口。(類單繼承,代理對象默認繼承Proxy類
  • 動態代理的第二個參數,接口內部的方法會被代理對象重寫,而後調用第三個參數的invoke方法。

上面兩個也是動態代理的原理了。咱們來仔細看一下咱們的run()方法,也就是咱們代理對象要實現的接口

public final void run() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
  • 調用了父類的h,父類的hInvocationHandler,而後調用了invoke方法執行了咱們的執行邏輯。

這個就是動態代理的所有實現過程

還有一個很是牛逼的點,它怎麼生成的這個代理類。來看一下代理的全過程

圖中的ASM就是爲咱們動態生成一個代理類的工具,它直接操做了Class字節碼的二進制,而後建立了一個代理類,返回給咱們。

Java動態代理就聊到這裏了。下面看一看CGLIbAOP

2.2 CGLIB動態代理

彌補了Java動態代理的不足,CGLIB動態代理能夠代理類。它直接建立了一個被代理對象的子類,實現了對其的代理過程。咱們來看一下它的代理過程

//打印生成的代理對象,放置於當前項目下
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, ".");
        //建立Enhancer對象,相似於JDK動態代理的Proxy類,下一步就是設置幾個參數
        Enhancer enhancer = new Enhancer();
        //設置目標類的字節碼文件
        enhancer.setSuperclass(Tank.class);
        //設置回調函數
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                methodProxy.invokeSuper(o,objects);
                return null;
            }
        });

        //這裏的creat方法就是正式建立代理類
        Tank proxyDog = (Tank)enhancer.create();
        //調用代理類的eat方法
        proxyDog.tank();

仍是和Java動態代理類似,傳入一個須要代理的Class,設置代理的回調函數。而後調用create建立一個代理對象,調用代理對象的方法。

代理第一行能夠輸出代理對象,會生成三個代理對象。

查看中間那個,能夠看到咱們被代理對象的方法

public class Tank$$EnhancerByCGLIB$$a4ec679a extends Tank implements Factory {
    //構造方法
    public Tank$$EnhancerByCGLIB$$a4ec679a() {
        CGLIB$BIND_CALLBACKS(this);
    }
    //被代理方法
    final void tank() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            //調用加強的方法
            var10000.intercept(this, CGLIB$tank$0$Method, CGLIB$emptyArgs, CGLIB$tank$0$Proxy);
        } else {
            super.tank();
        }
    }
}

在以前的CGLIB動態代理實現中,咱們看到了攔截的回調中傳入了四個參數,從上面的源碼中能夠看到對應參數的做用。

  • Object o表明生成的代理對象
  • Method method表明當前代理對象調用的方法
  • Object[] objects表明方法的參數
  • MethodProxy methodProxy咱們調用方法的方法代理,它沒有使用Java自己的反射,而是動態生成一個新的類,(繼承FastClass),向類中寫入委託類實例直接調用方法的語句。

咱們能夠看一下superinvoke的源碼

public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            this.init();
            MethodProxy.FastClassInfo fci = this.fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        }
    }

private static class FastClassInfo {
        FastClass f1;
        FastClass f2;
        int i1;
        int i2;

        private FastClassInfo() {
        }
    }

一個圖理解CgLib動態代理過程

寫了這麼多,感受對於代理設計模式講解的篇幅不是很大,而是着重講解了動態代理的實現方式。總的而言,代理設計模式與咱們平常生活很是的接近,生活中的事物幾乎都在被代理,因此這個設計模式應該很好懂,因此着重講解了動態代理的實現方式。

相關文章
相關標籤/搜索