設計模式之代理模式(通過字節碼探究執行過程)

代理模式

代理模式的定義:代理模式給某一個對象提供一個代理對象,並由代理對象控制對原對象的引用。通俗的來講代理模式就是我們生活中常見的中介。

1、靜態代理

我們有一個接口Movable,實現該接口的類都是可移動的:

interface Movable{
    void move();
}

public class Car implements Movable {
    @Override
    public void move() {
        System.out.println("Car move...");
        //模擬移動的時間,隨機10秒以內
        try {
            TimeUnit.SECONDS.sleep(new Random().nextInt(10));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class Bicycle implements Movable {
    @Override
    public void move() {
        System.out.println("Bicycle move...");
        //模擬移動的時間,隨機10秒以內
        try {
            TimeUnit.SECONDS.sleep(new Random().nextInt(10));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

現在想要實現的功能是在不改動源碼的情況下得到移動的時間,我們只能生成一箇中間類,將Movale的類組合進去,這個類也實現Movale接口,但是move方法中在調用m之前之後完成相應的功能:

class MoveTime implements Movable{

    Movable m;

    public MoveTime(Movable m) {
        this.m = m;
    }

    @Override
    public void move() {
        long s = System.currentTimeMillis();
        m.move();
        long e = System.currentTimeMillis();
        System.out.println("move time: "+(e-s));
    }
}

main函數,當要得到Car的move時間時,new Car,當想得到Bicycle的move時間時,new Bicycle:

public class Main {
    public static void main(String[] args) {
        MoveTime c = new MoveTime(new Car());
        c.move();
    }
}

MoveTime是代理類,Car、Bicycle都是被代理的對象。

靜態代理實現了在不改變原有代碼的情況下,完成對已有功能的增強,所謂增強,就是在完成原有功能的基礎上添加新的功能。

靜態代理的缺點是一個代理類只能實現一類接口的代理,當我們要對除Movale之外的接口類代理時,需要自己再手寫一個代理類,一類接口對應一個代理類,這樣做十分麻煩,於是就有了動態代理。

2、動態代理

動態代理是靜態代理的一種進化,靜態代理的缺點已經很明顯,動態代理解決了這個問題。

首先需要明確的是,動態代理解決的是代理類的問題,那它是怎麼實現代理類的生成呢?先看代碼:

//接口與Car類完全不動
interface Movable{
    void move();
}

public class Car implements Movable {
    @Override
    public void move() {
        System.out.println("Car move...");
        //模擬移動的時間,隨機10秒以內
        try {
            TimeUnit.SECONDS.sleep(new Random().nextInt(10));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

主函數調用:

    public static void main(String[] args) {
        System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        Car car = new Car();
        Movable m = (Movable) Proxy.newProxyInstance
                (
                        Car.class.getClassLoader(),
                        Car.class.getInterfaces(),
                        new MoveTimeProxy(car)
                );
        m.move();
    }

Movable m = (Movable) Proxy.newProxyInstance(Car.class.getClassLoader(),Car.class.getInterfaces(),new MoveTimeProxy(car));

這句話是通過newProxyInstance方法生成一個代理類,其中的參數:

  • 指定用哪個類加載器加載(ClassLoader loader
  • 指定該代理類要實現哪個接口(Class<?>[] interfaces
  • 指定調用處理器。(InvocationHandler h

其中最主要的是InvocationHandler,這個類主要實現處理過程,正如靜態代理中的計算時間等功能,都通過這個類來實現:

//聲明一個實現InvocationHandler接口的類,並重寫invoke方法。
public class MoveTimeProxy implements InvocationHandler {

    Object obj;

    public MoveTimeProxy(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long s = System.currentTimeMillis();
        Object result = method.invoke(obj,args);
        long e = System.currentTimeMillis();
        System.out.println("move time: "+(e-s));
        return result;
    }
}

主函數調用結果:

//Car move...
//move time: 5000

不過我們反過來再看主函數,也沒有調用MoveTimeProxy中的invoke方法啊,在哪裏調用的呢?

執行過程分析:

System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");這句話是將動態代理生成的類進行保存。當執行後會發現:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-1bSHwN9n-1590484407375)(C:\Users\秦\AppData\Roaming\Typora\typora-user-images\1590483472668.png)]

生成了一個$Proxy0.class的文件,這個文件就是動態代理中間生成的文件,我們通過IDEA的反編譯可以看到這個類的全貌:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package proxy.dynamicsproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

final class $Proxy0 extends Proxy implements Movable {
    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  {
        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 move() 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  {
        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 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);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("proxy.dynamicsproxy.Movable").getMethod("move");
            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());
        }
    }
}

這個類繼承自Proxy,實現了Movable接口。

而這個類的構造函數:

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

其中的參數是InvocationHandler,而我們自己寫的處理類也是實現了InvocationHandler這個接口,所以這裏的var1就是我們寫的MoveTime類。我們再點進去super看一眼:

protected Proxy(InvocationHandler h) {
    Objects.requireNonNull(h);
    this.h = h;
}

此時將我們的MoveTime類對象已經傳給了this.h。記住這裏,下面會講到。

再回來看.class文件,其中有move方法:

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

這個方法中調用的是super.h.invoke,驚不驚喜,意不意外,super.h就是我們傳入的MoveTime類對象,調用它的invoke,就是我們自己寫的那個invoke方法

所以大致流程是:當我們調用動態代理生成的類的move方法時,實則調用的是$Proxy0這個對象的的move方法,而這個方法中負責調用我們自己寫的處理函數,也就是我們自己寫的invoke方法。

而這個類是怎麼生成的呢?

採用java 的asm框架,直接對java字節碼進行操作,從而生成一個新類。

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