設計模式,結構模式之代理模式

1 概述

代理模式(Proxy Pattern)是Javaer們最熟悉的設計模式之一,大名鼎鼎的AOP就是通過代理模式來實現的。

2 代理模式

現實中,如果要邀請某個明星參加活動,我們不是跟這個明星直接溝通,而是找他的經紀人。因爲明星只需要負責表演就可以了,其他的事情由經紀人來安排。代理模式就是類似思想的體現:構造一個代理對象作爲中間層,當我們需要調用某個功能時,不是直接調用功能本身,而是通過代理對象完成請求轉發。這樣做的好處是:

  1. 實現了客戶端與服務之間的解藕
  2. 職責分離,服務方可以只專注與自己的主邏輯,而把一些擴展的邏輯放在代理對象中去實現

3 案例

看一個例子。定義一個Audience接口,有watchFilm()的功能,同時定義一個實現類給予基本的實現:

public interface Audience {
    void watchFilm();
}

public class AudienceImpl implements Audience {
    String name;
    public AudienceImpl(String name) {
        this.name = name;
    }

    @Override
    public void watchFilm() {
        System.out.println(name + " is watching film.");
    }

}

在此之上,需要增加一個功能,統計觀影人數

// 模擬dao層,提供統計觀影人數的方法
public class StatisticDao {
    private static StatisticDao instance = new StatisticDao();
    AtomicLong audienceNumber = new AtomicLong();
    private StatisticDao(){};

    public void incrAudienceNumber() {
        audienceNumber.getAndIncrement();
    }

    public void showAudienceNumber() {
        System.out.println(audienceNumber.get() + " audiences have watched the film.");
    }

    public static StatisticDao newInstance() {
        return instance;
    }
}

最簡單的做法當然是直接在AudienceImpl類裏面做修改。但是嚴格來說,統計觀影人數看電影是兩個功能,這違反了單一職責原則。而且如果以後需要增加其他功能,還是需要修改類本身,不易於維護。
如果使用代理模式,能很好地解決這個問題。

3.1 靜態代理

AudienceImpl定義一個代理對象,把統計觀影人數的功能放在代理對象中來做:

public class Test {
    public static void main(String[] args) {
        // 獲取的是代理類
        Audience nightField = new AudienceProxy(new AudienceImpl("Night Field"));
        Audience rocky = new AudienceProxy(new AudienceImpl("Rocky"));
        nightField.watchFilm();
        rocky.watchFilm();

        StatisticDao.newInstance().showAudienceNumber();
    }
}

// Proxy需要實現和 被代理類 相同的接口
public class AudienceProxy implements Audience {
    // 持有被代理對象
    Audience targetAudience;
    StatisticDao statisticDao = StatisticDao.newInstance();

    AudienceProxy(Audience targetAudience) {
        this.targetAudience = targetAudience;
    }

    @Override
    public void watchFilm() {
        // 接口的實現,其實就是調用了被代理類的方法
        targetAudience.watchFilm();
        // 額外增加了統計人數的功能
        statisticDao.incrAudienceNumber();
    }
}

輸出:

Night Field is watching film.
Rocky is watching film.
2 audiences have watched the film.

上述例子是靜態代理的實現方式,通過增加一個代理對象,在不修改原有邏輯的情況下,新增了功能。
但是靜態代理有一個弊端,就是代理對象是靜態的class無法動態擴展。更常見的例子是,需要在工程中某些特定類的方法前後添加log,如果用靜態代理的方法來實現的話,需要給所有的這些類都新建一個代理對象,這個工作量無疑是巨大的。於是動態代理應運而生。(使用AspectJ通過編譯器來實現切面模塊的織入,也算是一種靜態代理,但日常使用不多,本文不作考慮)。

3.2 動態代理–JDK InvocationHandler

自從JDK1.3開始,Java原生支持了動態代理。所謂動態代理,就是在運行時(runtime)生成代理對象。下面例子是用JDK動態代理來實現統計觀影人數的功能:

public class Test {
    public static void main(String[] args) {
        // 獲取的是代理類
        Audience nightField = new AudienceHandler(new AudienceImpl("Night Field")).getProxy();
        Audience rocky = new AudienceHandler(new AudienceImpl("Rocky")).getProxy();
        nightField.watchFilm();
        rocky.watchFilm();

        StatisticDao.newInstance().showAudienceNumber();
    }
}

// Proxy實現InvocationHandler接口
public class AudienceHandler implements InvocationHandler {
    // 持有被代理對象
    Audience targetAudience;
    StatisticDao statisticDao = StatisticDao.newInstance();

    AudienceHandler(Audience targetAudience) {
        this.targetAudience = targetAudience;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 調用被代理類的方法
        Object obj = method.invoke(targetAudience, args);
        // 額外增加了統計人數的功能
        statisticDao.incrAudienceNumber();
        return obj;
    }

    public Audience getProxy() {
        // newProxyInstance()方法,會在運行時構建出一個代理類,
        // 可以看到,被代理對象的接口是方法的第二個參數,所以要求被代理對象必須實現接口
        return (Audience) Proxy.newProxyInstance(targetAudience.getClass().getClassLoader(), targetAudience.getClass().getInterfaces(), this);
    }
}

輸出:

Night Field is watching film.
Rocky is watching film.
2 audiences have watched the film.

JDK提供了InvocationHandler接口,可以在invoke()方法裏面自定義代理對象的邏輯,在上例中,我們額外實現了統計觀影人數的功能。Proxy類的newProxyInstance()方法可以返回一個代理對象,需要三個參數:

  1. 類加載器:代理對象通過被代理對象的類加載器,在運行時動態生成。
  2. 被代理對象的接口:JDK動態代理原理與靜態代理類似,需要被代理對象實現接口,這也是此種代理方式的限制。
  3. InvocationHandler對象:最終代理對象調用的是InvocationHandlerinvoke()方法。

JDK動態代理會在JVM中創建類似com.sun.proxy.$Proxy0.class代理對象,通過反編譯可以看到,實現方式跟靜態代理很相似:

public final class $Proxy0 extends Proxy implements Audience {
    // 對應了equals(), hashcode(), toString(), watchFilm()幾個方法
    private static Method m1;
    private static Method m0;
    private static Method m3;
    private static Method m2;

    // InvocationHandler作爲構造方法的參數傳進來
    public =$Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    // equals()方法也會被代理,調用InvocationHandler調用invoke()方法
    // invoke()方法中可以調用被代理對象的方法,同時可以增加額外的邏輯
    public final boolean equals(Object var1) throws  {
        try {
            return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    // hashCode()方法也會被代理,調用InvocationHandler調用invoke()方法
    // invoke()方法中可以調用被代理對象的方法,同時可以增加額外的邏輯
    public final int hashCode() throws  {
        try {
            return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    // 代理類的watchFilm()方法,最終是調用InvocationHandler調用invoke()方法
    // invoke()方法中可以調用被代理對象的watchFilm()方法,同時增加了統計觀影人數的功能
    public final void watchFilm() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null));
        } catch (RuntimeException | Error var4) {
            throw var4;
        } catch (Throwable var5) {
            throw new UndeclaredThrowableException(var5);
        }
    }

    // toString()方法也會被代理,調用InvocationHandler調用invoke()方法
    // invoke()方法中可以調用被代理對象的方法,同時可以增加額外的邏輯
    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);
        }
    }

    static {
        try {
        	// 靜態塊初始化4個方法
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            m3 = Class.forName("cn.com.nightfield.patterns.structural.proxy.Audience").getMethod("watchFilm", new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

JDK動態代理可以動態地生成代理對象,比靜態代理方便得多,但是要求被代理對象必須實現接口,否則無法進行代理。對於這一類情況,CGLib(Code Generation Library)提供瞭解決方案。

3.3 動態代理–CGLib MethodInterceptor

CGLib是一個強大的代碼生成類庫,可以用來動態擴展/創建Java類。其底層依賴於一個Java字節碼操作框架ASM,其作者熟讀JVM規範,使得ASM類庫可以直接以二進制的形式修改類或動態生成類。
CGLib同樣提供了動態代理的實現方式:

public class Test {
    public static void main(String[] args) {
        // 獲取的是代理類
        Audience nightField = new AudienceInterceptor(new AudienceImpl("Night Field")).getProxy();
        Audience rocky = new AudienceInterceptor(new AudienceImpl("Rocky")).getProxy();
        nightField.watchFilm();
        rocky.watchFilm();

        StatisticDao.newInstance().showAudienceNumber();
    }
}

public class AudienceInterceptor implements MethodInterceptor {
    // 持有被代理對象
    Audience targetAudience;
    StatisticDao statisticDao = StatisticDao.newInstance();

    AudienceInterceptor(Audience targetAudience) {
        this.targetAudience = targetAudience;
    }

    // 方法會動態生成一個代理對象,可以看到,過程中需要指定代理對象的父類
    // 因爲CGLib生成的動態代理,是被代理對象的子類,
    public Audience getProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetAudience.getClass());
        enhancer.setCallback(this);
        return (Audience) enhancer.create(new Class[]{String.class}, new Object[]{ReflectUtil.getField(targetAudience, "name")});
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 調用被代理類的方法
        Object ret = proxy.invokeSuper(obj, args);
        // 額外增加了統計人數的功能
        statisticDao.incrAudienceNumber();
        return ret;
    }
}

輸出:

Night Field is watching film.
Rocky is watching film.
2 audiences have watched the film.

CGLib的方式實現動態代理,需要實現MethodInterceptor接口,並在intercept()方法中處理額外的邏輯。因爲代理對象會通過回調(Callback)的方式,來調用intercept()方法。
通過CGLib生成的代理對象,其實是被代理對象的一個子類,調用被代理方法時,用的是MethodProxy.invokeSuper(obj, args)方法。所以,用CGLib的方式實現的代理模式也是有限制的:不能代理final修飾的類和方法,不能代理private的方法。

通過反編譯,我們也能一窺CGLib生成代理類的真容:

// 代理對象繼承了被代理對象AudienceImpl
public class AudienceImpl$$EnhancerByCGLIB$$570ee29d extends AudienceImpl implements Factory {
    private boolean CGLIB$BOUND;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    // 我們定義的MethodInterceptor
    private MethodInterceptor CGLIB$CALLBACK_0;
    // 各代理方法與MethodProxy對象,除了watchFilm(),還能代理Object類中的方法
    private static final Method CGLIB$watchFilm$0$Method;
    private static final MethodProxy CGLIB$watchFilm$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$finalize$1$Method;
    private static final MethodProxy CGLIB$finalize$1$Proxy;
    private static final Method CGLIB$equals$2$Method;
    private static final MethodProxy CGLIB$equals$2$Proxy;
    private static final Method CGLIB$toString$3$Method;
    private static final MethodProxy CGLIB$toString$3$Proxy;
    private static final Method CGLIB$hashCode$4$Method;
    private static final MethodProxy CGLIB$hashCode$4$Proxy;
    private static final Method CGLIB$clone$5$Method;
    private static final MethodProxy CGLIB$clone$5$Proxy;

    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        // 代理對象
        Class var0 = Class.forName("cn.com.nightfield.patterns.structural.proxy.cgLib.AudienceImpl$$EnhancerByCGLIB$$570ee29d");
        Class var1;
        // 初始化Object類中的方法與對應的MethodProxy對象
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$finalize$1$Method = var10000[0];
        CGLIB$finalize$1$Proxy = MethodProxy.create(var1, var0, "()V", "finalize", "CGLIB$finalize$1");
        CGLIB$equals$2$Method = var10000[1];
        CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
        CGLIB$toString$3$Method = var10000[2];
        CGLIB$toString$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
        CGLIB$hashCode$4$Method = var10000[3];
        CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4");
        CGLIB$clone$5$Method = var10000[4];
        CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
        // 初始化watchFilm方法與對應的MethodProxy對象
        CGLIB$watchFilm$0$Method = ReflectUtils.findMethods(new String[]{"watchFilm", "()Ljava/lang/Void;"}, (var1 = Class.forName("cn.com.nightfield.patterns.structural.proxy.cgLib.AudienceImpl")).getDeclaredMethods())[0];
        CGLIB$watchFilm$0$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "watchFilm", "CGLIB$watchFilm$0");
    }

    // 代表了被代理類的watchFilm()方法,沒有額外邏輯,單純調用watchFilm()方法
    final String CGLIB$watchFilm$0() {
        return super.watchFilm();
    }

    // 代理類中的watchFilm()方法
    public final String watchFilm() {
        // 我們定義的MethodInterceptor
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }
        
        if (var10000 != null) {
            // 回調方法,調用自定義的MethodInterceptor中intercept()方法
            var10000.intercept(this, CGLIB$watchFilm$0$Method, CGLIB$emptyArgs, CGLIB$watchFilm$0$Proxy);
        } else {
            // 如果MethodInterceptor不存在,則直接調用被代理方法
            super.watchFilm();
        }
    }

    /* 
     * 以下Object類中的的方法與watchFilm()方法邏輯類似
     */ 
    final void CGLIB$finalize$1() throws Throwable {
        super.finalize();
    }

    protected final void finalize() throws Throwable {
        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$finalize$1$Method, CGLIB$emptyArgs, CGLIB$finalize$1$Proxy);
        } else {
            super.finalize();
        }
    }

    final boolean CGLIB$equals$2(Object var1) {
        return super.equals(var1);
    }

    public final boolean equals(Object var1) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            Object var2 = var10000.intercept(this, CGLIB$equals$2$Method, new Object[]{var1}, CGLIB$equals$2$Proxy);
            return var2 == null ? false : (Boolean)var2;
        } else {
            return super.equals(var1);
        }
    }

    final String CGLIB$toString$3() {
        return super.toString();
    }

    public final String toString() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        return var10000 != null ? (String)var10000.intercept(this, CGLIB$toString$3$Method, CGLIB$emptyArgs, CGLIB$toString$3$Proxy) : super.toString();
    }

    final int CGLIB$hashCode$4() {
        return super.hashCode();
    }

    public final int hashCode() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            Object var1 = var10000.intercept(this, CGLIB$hashCode$4$Method, CGLIB$emptyArgs, CGLIB$hashCode$4$Proxy);
            return var1 == null ? 0 : ((Number)var1).intValue();
        } else {
            return super.hashCode();
        }
    }

    final Object CGLIB$clone$5() throws CloneNotSupportedException {
        return super.clone();
    }

    protected final Object clone() throws CloneNotSupportedException {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        return var10000 != null ? var10000.intercept(this, CGLIB$clone$5$Method, CGLIB$emptyArgs, CGLIB$clone$5$Proxy) : super.clone();
    }

    // 獲取MethodProxy對象的方法
    public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
        String var10000 = var0.toString();
        switch(var10000.hashCode()) {
        case -1574182249:
            if (var10000.equals("finalize()V")) {
                return CGLIB$finalize$1$Proxy;
            }
            break;
        case -508378822:
            if (var10000.equals("clone()Ljava/lang/Object;")) {
                return CGLIB$clone$5$Proxy;
            }
            break;
        case 509984470:
            if (var10000.equals("watchFilm()Ljava/lang/Void;")) {
                return CGLIB$watchFilm$0$Proxy;
            }
            break;
        case 1826985398:
            if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
                return CGLIB$equals$2$Proxy;
            }
            break;
        case 1913648695:
            if (var10000.equals("toString()Ljava/lang/String;")) {
                return CGLIB$toString$3$Proxy;
            }
            break;
        case 1984935277:
            if (var10000.equals("hashCode()I")) {
                return CGLIB$hashCode$4$Proxy;
            }
        }

        return null;
    }
    
    // 構造方法,綁定callback(MethodInterceptor)
    public AudienceImpl$$EnhancerByCGLIB$$570ee29d() {
        CGLIB$BIND_CALLBACKS(this);
    }

    public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
        CGLIB$THREAD_CALLBACKS.set(var0);
    }

    public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {
        CGLIB$STATIC_CALLBACKS = var0;
    }

    // 綁定callback(MethodInterceptor)
    private static final void CGLIB$BIND_CALLBACKS(Object var0) {
        AudienceImpl$$EnhancerByCGLIB$$570ee29d var1 = (AudienceImpl$$EnhancerByCGLIB$$570ee29d)var0;
        if (!var1.CGLIB$BOUND) {
            var1.CGLIB$BOUND = true;
            Object var10000 = CGLIB$THREAD_CALLBACKS.get();
            if (var10000 == null) {
                var10000 = CGLIB$STATIC_CALLBACKS;
                if (var10000 == null) {
                    return;
                }
            }

            var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
        }

    }

    public Object newInstance(Callback[] var1) {
        CGLIB$SET_THREAD_CALLBACKS(var1);
        AudienceImpl$$EnhancerByCGLIB$$570ee29d var10000 = new AudienceImpl$$EnhancerByCGLIB$$570ee29d();
        CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
        return var10000;
    }

    public Object newInstance(Callback var1) {
        CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1});
        AudienceImpl$$EnhancerByCGLIB$$570ee29d var10000 = new AudienceImpl$$EnhancerByCGLIB$$570ee29d();
        CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
        return var10000;
    }

    public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {
        CGLIB$SET_THREAD_CALLBACKS(var3);
        AudienceImpl$$EnhancerByCGLIB$$570ee29d var10000 = new AudienceImpl$$EnhancerByCGLIB$$570ee29d;
        switch(var1.length) {
        case 0:
            var10000.<init>();
            CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
            return var10000;
        default:
            throw new IllegalArgumentException("Constructor not found");
        }
    }

    public Callback getCallback(int var1) {
        CGLIB$BIND_CALLBACKS(this);
        MethodInterceptor var10000;
        switch(var1) {
        case 0:
            var10000 = this.CGLIB$CALLBACK_0;
            break;
        default:
            var10000 = null;
        }

        return var10000;
    }

    public void setCallback(int var1, Callback var2) {
        switch(var1) {
        case 0:
            this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
        default:
        }
    }

    public Callback[] getCallbacks() {
        CGLIB$BIND_CALLBACKS(this);
        return new Callback[]{this.CGLIB$CALLBACK_0};
    }

    public void setCallbacks(Callback[] var1) {
        this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
    }

    static {
        CGLIB$STATICHOOK1();
    }
}

對比JDKCGLib產生的代理對象相對繁雜,但細看下,兩者的思路都是一樣的:代理對象實現/重寫被代理對象對象中的方法,並回調InvocationHandler/MethodInterceptor中自定義的邏輯,調用被代理方法

除了代理對象CGLib同時還會生成一系列FastClassJDK動態代理是通過反射的方式去調用被代理方法的,而衆所周知,反射調用的性能並不好。所以爲了避免反射,CGLib提供了FastClass機制(反正我能動態生成對象,索性一次生成多一些額外的對象來提高性能)。FastClass爲各個方法構建了索引,訪問被代理對象的方法時,只需按索引查找,即可快速調用,方式大致如下:

public class MethodProxy {
    // 通過invokeSuper調用被代理方法
    public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            init();
            // 獲取FastClass
            FastClassInfo fci = fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }
    private static class FastClassInfo {
        FastClass f1; // 被代理對象對應的FastClass
        FastClass f2; // 代理對象對應的FastClass
        int i1; // 被代理方法對應的index
        int i2; // 代理方法對應的index
    }
}

public class TargetInterfaceImpl$$FastClassByCGLIB$$d18f5d8e extends FastClass {
    public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        AudienceImpl var10000 = (AudienceImpl)var2;
        int var10001 = var1;

        try {
            // 各方法以索引的形式被管理
            switch(var10001) {
            case 0:
                // 根據索引,直接調用對應的方法,可以繞開反射
                var10000.watchFilm();
                return null;
            case 1:
                var10000.wait();
                return null;
            case 2:
                var10000.wait(((Number)var3[0]).longValue(), ((Number)var3[1]).intValue());
                return null;
            case 3:
                var10000.wait(((Number)var3[0]).longValue());
                return null;
            case 4:
                return new Boolean(var10000.equals(var3[0]));
            case 5:
                return var10000.toString();
            case 6:
                return new Integer(var10000.hashCode());
            case 7:
                return var10000.getClass();
            case 8:
                var10000.notify();
                return null;
            case 9:
                var10000.notifyAll();
                return null;
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }

        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }
}

相對於JDKCGLib由於FastClass機制的存在,在生成代理的過程中,效率較低;但在生成代理之後,代理過程的執行效率會更高。

4 Spring AOP

通過上面的分析,可以大致猜想出SpringAOP到底是怎麼實現的了。InvocationHandlerMethodInterceptor使得我們很方便地在方法的特定位置添加如事務,日誌等切面邏輯(Before,After,Around,AfterThrowing,AfterReturning)。Spring中對兩種方式的動態代理都有實現:

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

	@Override
	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        // 一般我們會通過配置 proxyTargetClass 來控制使用JDK還是CGLib,默認是false,也就是使用JdkDynamicAopProxy
        // 因爲運行時CGLib的效率相對於JDK會略高,所以叫 isOptimize()
		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
			Class<?> targetClass = config.getTargetClass();
			if (targetClass == null) {
				throw new AopConfigException("TargetSource cannot determine target class: " +
						"Either an interface or a target is required for proxy creation.");
			}
            // 如果目標類是接口,依然會用JDK動態代理
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
            // CGLib代理
			return new ObjenesisCglibAopProxy(config);
		}
		else {
            // JDK代理
			return new JdkDynamicAopProxy(config);
		}
	}
}

下面簡單舉例JdkDynamicAopProxy

// 實現類InvocationHandler接口
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
    @Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 切面邏輯委託給MethodInvocation來實現
		MethodInvocation invocation;

		TargetSource targetSource = this.advised.targetSource;
		Class<?> targetClass = null;
		Object target = null;

		try {
			...
			Object retVal;

			// 被代理類
			target = targetSource.getTarget();
			if (target != null) {
				targetClass = target.getClass();
			}

			// 得到方法的interception chain,即切面列表
			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

			
			if (chain.isEmpty()) {
				// 直接調用目標方法
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
			}
			else {
				// 通過ReflectiveMethodInvocation,來鏈式調用切面邏輯
				invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
				// 執行切面邏輯,與被代理類的主邏輯,並得到返回值
				retVal = invocation.proceed();
			}

			...
			return retVal;
		}
		finally {
			if (target != null && !targetSource.isStatic()) {
				// 多線程控制
				targetSource.releaseTarget(target);
			}
			if (setProxyContext) {
				// 多線程控制
				AopContext.setCurrentProxy(oldProxy);
			}
		}
	}
}

4 總結

代理模式分爲靜態代理動態代理動態代理又有兩種實現方式:JDKCGLib代理模式AOP的基礎,是面向對象設計中非常重要的一種設計模式。

文中例子的github地址

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