Java代理模式之從源碼分析靜態代理與動態代理

什麼是代理模式

這裏我直接引用百度百科裏面的一句話。java

代理模式的定義:爲其餘對象提供一種代理以控制對這個對象的訪問。在某些狀況下,一個對象不適合或者不能直接引用另外一個對象,而代理對象能夠在客戶端和目標對象之間起到中介的做用。git

這些文字上的東西我也說的不太清楚,在下面我會盡量使用代碼來讓你們明白靜態代理和動態代理這兩種方式的原理!說清楚原理以後我再說下代理模式能幫咱們作什麼??github

tip:這裏我用的IntelliJ IDEA寫的java項目來作的示例,有想作示例的同窗千萬不要用Android項目來寫,源代碼會有所不一樣!!!還有接下來文章比較長,但願你們能耐心看完,看完收穫是確定有的。編程

靜態代理

這裏我使用一個明星和經紀人關係的例子來作一個比較形象的代碼說明,但願你們能夠看代碼就明白其中的關係。設計模式

靜態代理必要的元素

首先,咱們要實現一個代理模式,那有三個元素是必定要有的,且這三個元素不論是靜態代理仍是動態代理都是必須的,這裏我先以靜態代理爲例作下說明。數組

1.行爲接口類(接口,定義行爲)

這個元素是一個行爲接口類,主要是定義各類行爲方法,這裏咱們以一位明星掌握的技能爲例,如(某明星如今只有一個唱歌的技能):bash

/**
 * desc: 定義一些明星擁有的技能行爲
 *
 * @author Wepon.Yan
 * created at 2019/1/23 下午4:16
 */
public interface ISkillAction {

    /**
     * 唱歌
     */
    void sing();
}
複製代碼

2.真實對象(這裏以明星爲例)

這裏咱們須要一個真實的對象,主要是提供給代理者進行操做。app

/**
 * desc: 明星 --- 真實對象
 *
 * @author Wepon.Yan
 * created at 2019/1/23 下午4:15
 */
public class Star implements ISkillAction {

    @Override
    public void sing() {
        System.out.println("明星開始唱歌了。");
    }
}
複製代碼

3.代理者(這裏以經紀人爲例)

這裏是咱們的代理者,也就是明星經紀人,須要負責明星的各類行程規劃和安排,那麼用編程的思想來講的話,咱們確定是要持有這個"明星"的一份引用的,固然了,如今咱們的明星還只會唱歌這一項技能(sing()),那麼咱們如今須要代理的也就只有sing()這一個行爲方法了,這裏是須要跟明星同樣實現共同的接口的(ISkillAction):框架

/**
 * desc: 明星的經紀人 --- 代理
 *
 * @author Wepon.Yan
 * created at 2019/1/23 下午4:20
 */
public class Agent implements ISkillAction {

    /**
     * 經紀人是須要持有一份目標對象(真實對象---明星)的引用的。
     */
    private Star mStar;

    public Agent(Star star) {
        mStar = star;
    }

    @Override
    public void sing() {
        // 經紀人根據明星的行程計劃作好本次的規劃。
        System.out.println("經紀人首先要安排好本次行程計劃。");
        // 明星唱歌前經紀人要跟活動商作好相關的佈置。
        System.out.println("經紀人跟活動商進行唱歌前佈置。");
        // 明星開始唱歌。
        mStar.sing();
        // 明星唱完歌后經紀人要作好收尾工做(例如把出演費收到帳等等)。
        System.out.println("經紀人跟活動商作收尾工做(例如把出演費收到帳等等)。");
        // 本次行程結束,經紀人須要更新行程計劃
        System.out.println("本次行程結束,經紀人須要更新行程計劃。");
    }
}
複製代碼

靜態代理的使用

以上咱們已經有了這三個必要的元素了,那咱們能夠開始進行代理模式的使用了,使用方式以下:ide

public class JavaProxyTest {

    public static void main(String[] args) {

        // 使用靜態代理模式
        useStaticProxy();

    }

    /**
     * 靜態代理模式的使用
     */
    private static void useStaticProxy() {
        // 要先有一份真實對象(明星對象)
        Star star = new Star();
        // 而後代理者(經紀人)要持有這個真實對象
        Agent agent = new Agent(star);
        // 這樣代理者就能夠進行這些接口行爲的代理操做了
        // 就比如有些商業活動負責人先找到某明星經紀人,經過經紀人成功邀請到某明星來進行演出。
        agent.sing();
    }
}
複製代碼

這裏我註釋應該是比較清楚了的,首先咱們確定是要有一個真實對象的實例的(明星本人),而後咱們能夠經過代理者(經紀人)進行代理操做。

輸出的日誌以下:

這裏咱們能夠看到,經過這個代理者經紀人,咱們的活動商成功請到咱們的明星進行了一次唱歌出演,咱們的明星只須要負責唱歌這個行爲,唱歌先後的佈置安排或者其餘一些行爲的插入等等,都是能夠經過代理者完成的。

靜態代理的擴展

經過上面咱們能夠看到一個簡單的靜態代理就完成了,可是這個時候可能有些機智的同窗會想到一些問題,如:

1.行爲接口類方法增長

在正常狀況下,咱們的行爲接口須要定義的方法是可能有多個的(明星可能會唱歌,會跳舞等等),那麼咱們上面提到的三個元素就會發生一點改變了,代碼以下:

/**
 * desc: 定義一些明星擁有的技能行爲
 *
 * @author Wepon.Yan
 * created at 2019/1/23 下午4:16
 */
public interface ISkillAction {

    /**
     * 唱歌
     */
    void sing();

    /**
     * 跳舞
     */
    void dance();
}
複製代碼
/**
 * desc: 明星 --- 真實對象
 *
 * @author Wepon.Yan
 * created at 2019/1/23 下午4:15
 */
public class Star implements ISkillAction {

    @Override
    public void sing() {
        System.out.println("明星開始唱歌了。");
    }

    @Override
    public void dance() {
        System.out.println("明星開始跳舞了。");
    }
}
複製代碼
/**
 * desc: 明星的經紀人 --- 代理
 *
 * @author Wepon.Yan
 * created at 2019/1/23 下午4:20
 */
public class Agent implements ISkillAction {

    /**
     * 經紀人是須要持有一份目標對象(真實對象---明星)的引用的。
     */
    private Star mStar;

    public Agent(Star star) {
        mStar = star;
    }

    @Override
    public void sing() {
        // 經紀人根據明星的行程計劃作好本次的規劃。
        System.out.println("經紀人首先要安排好本次行程計劃。");
        // 明星唱歌前經紀人要跟活動商作好相關的佈置。
        System.out.println("經紀人跟活動商進行唱歌前佈置。");
        // 明星開始唱歌。
        mStar.sing();
        // 明星唱完歌后經紀人要作好收尾工做(例如把出演費收到帳等等)。
        System.out.println("經紀人跟活動商作收尾工做(例如把出演費收到帳等等)。");
        // 本次行程結束,經紀人須要更新行程計劃
        System.out.println("本次行程結束,經紀人須要更新行程計劃。");
    }

    @Override
    public void dance() {
        // 經紀人根據明星的行程計劃作好本次的規劃。
        System.out.println("經紀人首先要安排好本次行程計劃。");
        // 明星開始跳舞。
        mStar.sing();
        // 本次行程結束,經紀人須要更新行程計劃
        System.out.println("本次行程結束,經紀人須要更新行程計劃。");
    }
}
複製代碼

從上面咱們能夠發現,共同行爲接口每多一個方法行爲,那咱們的代理者也就須要多實現一個代理方法,而且有可能咱們每一個代理方法先後都有一樣的操做,那麼咱們就須要在每一個方法裏面重複寫這些操做,這能夠說是靜態代理的一些缺點了,這些我會在介紹動態代理的時候經過代碼進行對比說明。

2.增長行爲接口類的數量

咱們的示例中明星只實現了一個技能接口類,可是實際狀況中通常不止實現一個,這裏咱們增長一個生活共同行爲類,以下:

/**
 * desc: 定義一些生活上的行爲方法
 *
 * @author Wepon.Yan
 * created at 2019/1/23 下午4:18
 */
public interface ILiveAction {

    /**
     * 吃早餐
     */
    void eatBreakfast();
}
複製代碼

而後咱們的明星實現了這個接口:

public class Star implements ISkillAction, ILiveAction {


    @Override
    public void sing() {
        System.out.println("明星開始唱歌了。");
    }

    @Override
    public void dance() {
        System.out.println("明星開始跳舞了。");
    }

    @Override
    public void eatBreakfast() {
        System.out.println("明星開始吃早餐了。");
    }
}
複製代碼

最後咱們的代理者(經紀人)也須要實現這些共同行爲接口類:

public class Agent implements ISkillAction, ILiveAction {

    /**
     * 經紀人是須要持有一份目標對象(真實對象---明星)的引用的。
     */
    private Star mStar;

    public Agent(Star star) {
        mStar = star;
    }

    @Override
    public void sing() {
        // 經紀人根據明星的行程計劃作好本次的規劃。
        System.out.println("經紀人首先要安排好本次行程計劃。");
        // 明星唱歌前經紀人要跟活動商作好相關的佈置。
        System.out.println("經紀人跟活動商進行唱歌前佈置。");
        // 明星開始唱歌。
        mStar.sing();
        // 明星唱完歌后經紀人要作好收尾工做(例如把出演費收到帳等等)。
        System.out.println("經紀人跟活動商作收尾工做(例如把出演費收到帳等等)。");
        // 本次行程結束,經紀人須要更新行程計劃
        System.out.println("本次行程結束,經紀人須要更新行程計劃。");
    }

    @Override
    public void dance() {
        // 經紀人根據明星的行程計劃作好本次的規劃。
        System.out.println("經紀人首先要安排好本次行程計劃。");
        // 明星開始跳舞。
        mStar.sing();
        // 本次行程結束,經紀人須要更新行程計劃
        System.out.println("本次行程結束,經紀人須要更新行程計劃。");
    }

    @Override
    public void eatBreakfast() {
        // 這裏就以經紀人進行早餐預訂作一個假設吧。
        System.out.println("經紀人幫明星預訂早餐。");
        // 明星開始吃早餐了。
        mStar.eatBreakfast();
        // 吃完後經紀人去幫明星結帳。
        System.out.println("經紀人去幫明星結帳。");
    }
}
複製代碼

經過這裏咱們又能夠發現,共同行爲接口實現的越多,咱們代理者要實現的也一樣須要增多,這樣仍是挺麻煩的。

動態代理

動態代理我這裏使用的是jdk提供的實現方式。

咱們先實現一下動態代理的寫法再來分析它的流程,在使用動態代理前咱們要有如下元素:

共同行爲接口類

首先咱們使用下靜態代理裏面的共同行爲接口類:

public interface ISkillAction {

    /**
     * 唱歌
     */
    void sing();

    /**
     * 跳舞
     */
    void dance();
}
複製代碼
public interface ILiveAction {

    /**
     * 吃早餐
     */
    void eatBreakfast();
}
複製代碼

InvocationHandler實現類

這個是jdk中提供的,做用是經過實現InvocationHandler接口建立本身的調用處理器,這裏我只對每一個行爲的調用先後進行一行日誌輸出,以下:

/**
 * desc: 建立本身的調用處理器
 *
 * @author Wepon.Yan
 * created at 2019/1/23 下午6:52
 */
public class DynamicProxyHandler implements InvocationHandler {

    /**
     * 持有一份真實對象的引用
     */
    private Object mObject;

    public DynamicProxyHandler(Object object) {
        mObject = object;
    }
    
     /**
     * 
     * @param proxy 代理類對象
     * @param method 方法
     * @param args 方法參數
     * @return 方法返回
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("代理前的統一操做");
        Object invoke;
        // 這裏能夠根據不一樣方法名進行不一樣的操做
        if ("sing".equals(method.getName())) {
            // 攔截下唱歌
            // 明星唱歌前經紀人要跟活動商作好相關的佈置。
            System.out.println("經紀人跟活動商進行唱歌前佈置。");
            // 調用真實對象的行爲
            invoke = method.invoke(mObject, args);
            // 明星唱完歌后經紀人要作好收尾工做(例如把出演費收到帳等等)。
            System.out.println("經紀人跟活動商作收尾工做(例如把出演費收到帳等等)。");
        }else{
            // 調用真實對象的行爲
            invoke = method.invoke(mObject, args);
        }

        System.out.println("代理後的統一操做");

        return invoke;
    }
}

複製代碼

動態代理的使用

而後咱們經過jdk提供的方式使用動態代理:

/**
     * 動態代理模式的使用
     */
    private static void useDynamicProxy() {
        // 要先有一份真實對象(明星對象)
        Star star = new Star();
        // 而後要有一個自定義的處理器
        DynamicProxyHandler dynamicProxyHandler = new DynamicProxyHandler(star);
        // 而後經過jdk方式動態生成代理對象proxy
        // 這裏是一個Object類型,根據不一樣行爲接口強轉
        Object proxy = Proxy.newProxyInstance(
                ClassLoader.getSystemClassLoader(),
                new Class[]{ISkillAction.class, ILiveAction.class},
                dynamicProxyHandler
        );

        // 進行各個行爲的代理,這裏須要進行不一樣行爲類型的轉換
        ((ISkillAction) proxy).sing();
        ((ISkillAction) proxy).dance();

        ((ILiveAction) proxy).eatBreakfast();

    }
複製代碼

而後咱們在控制檯看下日誌的打印狀況:

從上面能夠知道咱們代理是成功了的,可是這裏尚未完善,後面再補充,這裏可能有同窗會問了,爲何這麼寫就實現動態代理了呢?原理是什麼呢?不慌,下面咱們經過源碼和反編譯來追蹤分析一下:

動態代理原理分析

首先,咱們分析下這個Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)源碼,這個方法的註釋開頭是這麼說的:

* Returns an instance of a proxy class for the specified interfaces
 * that dispatches method invocations to the specified invocation
 * handler.
 * @param  loader  
            the class loader to define the proxy class
 * @param  interfaces   
            the list of interfaces for the proxy class
 *          to implement
 * @param  h   
            the invocation handler to dispatch method invocations to
 * @return  a proxy instance with the specified invocation handler of a
 *          proxy class that is defined by the specified class loader
 *          and that implements the specified interfaces
 
 簡單翻譯一下就是:
 * 返回指定接口的代理類的實例,將方法調用調度到指定的調用處理程序。
 * @param loader  用於定義代理類的類加載器
 * @param interfaces  要實現的代理類的接口列表
 * @param h  調度方法調用的調用處理程序
 * @return 具備代理類的指定調用處理程序的代理實例,
    該代理類的指定調用處理程序由指定的類裝入器定義並實現指定的接口
複製代碼

重點:返回指定接口的代理類的實例,將方法調用調度到指定的調用處理程序。

從這句話咱們結合代碼能夠看出,意思就是經過Proxy.newProxyInstance返回一個代理類的實例proxy,而後經過這個代理類proxy在進行方法調用的時候,實際又調度到了咱們指定的DynamicProxyHandler的invoke方法裏面進行處理。

問題來了:

1. 那到底是怎麼生成的代理類呢?

2. 生成的代理類又是長什麼樣子的呢?

3. 代理類又是怎麼將方法調用調度到咱們指定的調用處理程序的呢(InvocationHandler實現類)?

動態代理類是如何生成的

那麼咱們繼續分析Proxy.newProxyInstance()方法的代碼實現:

這裏我把一些沒用的註釋和不用分析的catch代碼去掉了,這裏主要是利用反射實現,以下。

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }
        
        //查找或生成指定的代理類Class對象。
        Class<?> cl = getProxyClass0(loader, intfs);
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            // 獲取代理類Class的構造函數。
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        // 若是做用域爲私有,setAccessible爲ture支持訪問
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            // 經過建立一個代理類的實例(這裏構造函數中傳入了咱們的調用處理器,至於緣由等下分析),並返回。
            return cons.newInstance(new Object[]{h});
            
            // catch部分的代碼我所有刪除了,不看。
        } catch (Exception e) {
           
        } 
    }

複製代碼

從上面的代碼能夠看到,最核心的是這一行代碼,查找或生成指定的代理類Class對象,而後咱們繼續盯着這個方法走下去。

Class<?> cl = getProxyClass0(loader, intfs);
複製代碼
/**
     * Generate a proxy class.  Must call the checkProxyAccess method
     * to perform permission checks before calling this.
     */
    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }
複製代碼

繼續看proxyClassCache的生成:

/**
     * a cache of proxy classes
     */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
複製代碼

上面有說經過ProxyClassFactory建立代理類,這裏直接看ProxyClassFactory類的構造

這裏就一個apply方法,咱們直接看apply方法,apply中關鍵點我用中文進行註釋了三次,請看下面註釋的地方,如:

/**
     * A factory function that generates, defines and returns the proxy class given
     * the ClassLoader and array of interfaces.
     */
    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        private static final String proxyClassNamePrefix = "$Proxy";
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            .......
            .......
            /*
             *這中間省略掉兩個大的for循環代碼,太多了,這裏不須要進行分析
             */
            .......
            .......

            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }
            
            long num = nextUniqueNumber.getAndIncrement();
             // 要生成的代理類的名稱。下面生成代理類的時候要用到的。
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

             // 如下代碼是生成指定的代理類。
             // 如下代碼是生成指定的代理類。
             // 如下代碼是生成指定的代理類。
             
             // 生成代理類Class byte數組
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
            //  這裏生成代理類Class返回
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                throw new IllegalArgumentException(e.toString());
            }
        }
    }
複製代碼

到了這一步,我順便Debug了一下這裏的代碼,如圖:

這裏咱們看到proxyName,也就是咱們生成的代理類的Name是com.sun.proxy.$Proxy0,爲了驗證一下,我在測試代碼中打印了一下Log,以下:

而後咱們繼續看ProxyGenerator.generateProxyClass(); 和 defineClass0(); 這兩個方法,發如今defineClass0()是一個native方法,那就沒啥好看的了

private static native Class<?> defineClass0(ClassLoader loader, String name,
                                                byte[] b, int off, int len);
                                                
複製代碼

這裏最核心的方法是生成ProxyGenerator.generateProxyClass()這個,咱們繼續看下去(這裏提示一下,下面追進去的源碼顯示的格式有問題,不是正常的源碼了,並且有不少我不會再解釋了,由於我也不能都看懂,只會用註釋來解釋一部分重要的地方,若是下面這部分看不懂或者不想看能夠直接跳過,直接看後面寫的動態生成的代理類的內容就ok,不會影響代理模式的理解):

ProxyGenerator.generateProxyClass()追進去後往下看,下面是ProxyGenerator類中的部份內容:


    // 這個值是一個系統變量,這裏是控制下面生成了代理類Class的時候是否要保存成文件 。
    private static final boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"));
    

    public static byte[] generateProxyClass(String var0, Class<?>[] var1) {
        return generateProxyClass(var0, var1, 49);
    }

    public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
        ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
        
        // 這裏是生成代理類Class byte數組的地方
        final byte[] var4 = var3.generateClassFile();
        
        // 這裏是判斷是否要對這個文件進行保存
        if (saveGeneratedFiles) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    try {
                        int var1 = var0.lastIndexOf(46);
                        Path var2;
                        if (var1 > 0) {
                            Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
                            Files.createDirectories(var3);
                            var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
                        } else {
                            var2 = Paths.get(var0 + ".class");
                        }

                        Files.write(var2, var4, new OpenOption[0]);
                        return null;
                    } catch (IOException var4x) {
                        throw new InternalError("I/O exception saving generated file: " + var4x);
                    }
                }
            });
        }

        // 返回byte數組
        return var4;
    }

複製代碼

從上面咱們看到有三條註釋的地方,如今咱們這裏先看下生成代理類Class byte數組的地方,也就是generateClassFile()這個方法,這個方法超級長,仍是同樣,請注意我註釋的地方就OK,我會標明大概意思,具體就不要太糾結了,頭髮都掉光了:

private byte[] generateClassFile() {
        // 首先是添加 hashCode(),equals(),toString()這些方法
        this.addProxyMethod(hashCodeMethod, Object.class);
        this.addProxyMethod(equalsMethod, Object.class);
        this.addProxyMethod(toStringMethod, Object.class);
        
        // 添加接口中的方法
        Class[] var1 = this.interfaces;
        int var2 = var1.length;

        int var3;
        Class var4;
        for(var3 = 0; var3 < var2; ++var3) {
            var4 = var1[var3];
            Method[] var5 = var4.getMethods();
            int var6 = var5.length;

            for(int var7 = 0; var7 < var6; ++var7) {
                Method var8 = var5[var7];
                this.addProxyMethod(var8, var4);
            }
        }

        Iterator var11 = this.proxyMethods.values().iterator();

        List var12;
        while(var11.hasNext()) {
            var12 = (List)var11.next();
            checkReturnTypes(var12);
        }

        Iterator var15;
        try {
        // 添加構造方法
            this.methods.add(this.generateConstructor());
            var11 = this.proxyMethods.values().iterator();

            while(var11.hasNext()) {
                var12 = (List)var11.next();
                var15 = var12.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
                    this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
                    this.methods.add(var16.generateMethod());
                }
            }
    
            // 添加靜態初始化方法
            this.methods.add(this.generateStaticInitializer());
        } catch (IOException var10) {
            throw new InternalError("unexpected I/O Exception", var10);
        }

        if (this.methods.size() > 65535) {
            throw new IllegalArgumentException("method limit exceeded");
        } else if (this.fields.size() > 65535) {
            throw new IllegalArgumentException("field limit exceeded");
        } else {
            this.cp.getClass(dotToSlash(this.className));
            this.cp.getClass("java/lang/reflect/Proxy");
            var1 = this.interfaces;
            var2 = var1.length;

            for(var3 = 0; var3 < var2; ++var3) {
                var4 = var1[var3];
                this.cp.getClass(dotToSlash(var4.getName()));
            }
            
            // 到了這裏要生成的代理類Class的基本信息就都完了,下面就是生成代理類的.class文件了,這裏我特意在網上查過,可是沒懂,涉及到Class文件的魔數這些東西,我也是剛接觸這些知識點,還在看,總之下面就是生成Class文件用的了,看不懂了 - -!!!
            this.cp.setReadOnly();
            ByteArrayOutputStream var13 = new ByteArrayOutputStream();
            DataOutputStream var14 = new DataOutputStream(var13);

            try {
                var14.writeInt(-889275714);
                var14.writeShort(0);
                var14.writeShort(49);
                this.cp.write(var14);
                var14.writeShort(this.accessFlags);
                var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
                var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
                var14.writeShort(this.interfaces.length);
                Class[] var17 = this.interfaces;
                int var18 = var17.length;

                for(int var19 = 0; var19 < var18; ++var19) {
                    Class var22 = var17[var19];
                    var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
                }

                var14.writeShort(this.fields.size());
                var15 = this.fields.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
                    var20.write(var14);
                }

                var14.writeShort(this.methods.size());
                var15 = this.methods.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
                    var21.write(var14);
                }

                var14.writeShort(0);
                return var13.toByteArray();
            } catch (IOException var9) {
                throw new InternalError("unexpected I/O Exception", var9);
            }
        }
    }
複製代碼

上面的這個方法就是我能跟到最後的方法了,在這裏拿到了Class byte數組,整個動態生成代理類的流程到這裏咱們就結束了!

霸特!!! 咱們尚未看到生成的代理類長什麼樣子啊?總要給咱們看下樣子吧?

好好好,繼續!!

而後咱們再看下 if (saveGeneratedFiles) {}這個地方,看這裏一堆var頭疼,可是這裏的代碼意思是if true的話會保存一份文件(追到這裏能夠猜想,文件應該是生成的代理類的.class文件了),那咱們仍是Debug一下這個地方,看有沒有進行保存,保存的文件又在哪裏?這是就扯到怎麼看查看生成的代理類的.class文件了,下面開個欄再說。

如何查看動態代理生成的代理類

1.修改系統變量值生成代理類

剛說到咱們要Debug一下 if (saveGeneratedFiles)這個地方,Debug結果以下:

這裏是一個false,那就沒有走到下面保存文件的地方了,這裏這個變量我在網上查了下,是能夠修改的,並且有意想不到的收穫,這裏慢點說,咱們先把這個值改成true繼續Debug看一下保存了什麼?

修改saveGeneratedFiles爲true:

就這樣就好了,記得放在前面,而後繼續Debug看,生效了:

而後咱們看下這個方法裏面:

經過這裏的代碼和path,咱們能夠看到這裏生成了一個$Proxy0.class的文件保存在這個path:com/sun/proxy/$Proxy0.class,那是否是這樣呢?

跑了一下,報錯了!!不慌,看下:

哦,這錯仍是很明顯的,那咱們把這些類實現下Serializable就行了:

好了,再跑一下,咱們能夠看到確實有生成文件了,運行一下代碼後能夠看到咱們的項目結構發生了改變:

這裏多出了一個.class文件,這個文件就是動態生成的代理類的.class文件,以前是沒有的,如今保存在了這個位置,咱們直接雙擊打開看一下,不容易啊,追了大半天終於要看到你了!!!

這裏代碼以下,一樣地,你們注意下我寫的註釋,大概就能明白這個類作了什麼了:

public final class $Proxy0 extends Proxy implements ISkillAction, ILiveAction {
    // 保存的每一個方法,利用反射獲取,具體的代碼在最下面的static{}塊中
    private static Method m1;
    private static Method m5;
    private static Method m3;
    private static Method m2;
    private static Method m4;
    private static Method m0;

    // 首先這裏的構造方法是傳入了一個自定義的處理器,這裏用的super,咱們看下super中作了什麼? 其實就是設置屬性h引用這個自定義的處理器,這個h很重要,在下面會用到。
    // protected Proxy(InvocationHandler h) {
    //    Objects.requireNonNull(h);
    //    this.h = h;
    // }

    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);
        }
    }

    // 這個是咱們的接口行爲中的方法(吃早餐),當咱們調用代理類的eatBreakfast()方法的時候就會走到這一步
    public final void eatBreakfast() throws  {
        try {
        // 這裏咱們看到調用了h.invoke方法,也就是調用了咱們自定義的處理器的方法。如下方法都是這樣的邏輯,就不一一註釋了。
            super.h.invoke(this, m5, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void dance() 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 void sing() throws  {
        try {
            super.h.invoke(this, m4, (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"));
            m5 = Class.forName("com.wepon.proxydemo.proxy.ILiveAction").getMethod("eatBreakfast");
            m3 = Class.forName("com.wepon.proxydemo.proxy.ISkillAction").getMethod("dance");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("com.wepon.proxydemo.proxy.ISkillAction").getMethod("sing");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}
複製代碼

獲取代理類Class byte[]寫入到文件

還記得以前源碼有註釋一個這樣的方法嗎?

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
            proxyName, interfaces, accessFlags);
複製代碼

咱們能夠經過這個方法拿到代理類Class的byte[]後本身保存成.class文件,代碼以下:

運行一下後能夠看到咱們的項目中生成了一個文件 :

這個文件打開後跟上面修改系統變量生成的文件內容是同樣的,不用懷疑我,是真的。

代理類怎麼將方法調用調度到咱們指定的調用處理程序的

從以上代碼能夠看到,咱們調用代理類的方法的時候,最終會調用到咱們自定義的處理器(我文中寫的這個DynamicProxyHandler)的invoke(Object proxy, Method method, Object[] args)方法,這裏咱們終於就知道爲何會調用到咱們寫的處理器裏面去了。

好了咱們再回顧一下這個DynamicProxyHandler,加深每一個元素之間的關聯印象:

/**
 * desc: 建立本身的調用處理器
 *
 * @author Wepon.Yan
 * created at 2019/1/23 下午6:52
 */
public class DynamicProxyHandler implements InvocationHandler, Serializable {

    /**
     * 持有一份真實對象的引用
     */
    private Object mObject;

    public DynamicProxyHandler(Object object) {
        mObject = object;
    }


    /**
     * 
     * @param proxy 代理類對象
     * @param method 方法
     * @param args 方法參數
     * @return 方法返回
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("代理前的統一操做");
        Object invoke;
        // 這裏能夠根據不一樣方法名進行不一樣的操做
        if ("sing".equals(method.getName())) {
            // 攔截下唱歌
            // 明星唱歌前經紀人要跟活動商作好相關的佈置。
            System.out.println("經紀人跟活動商進行唱歌前佈置。");
            // 調用真實對象的行爲
            invoke = method.invoke(mObject, args);
            // 明星唱完歌后經紀人要作好收尾工做(例如把出演費收到帳等等)。
            System.out.println("經紀人跟活動商作收尾工做(例如把出演費收到帳等等)。");
        }else{
            // 調用真實對象的行爲
            invoke = method.invoke(mObject, args);
        }

        System.out.println("代理後的統一操做");

        return invoke;
    }
}

複製代碼

好了,分析到這裏,就都結束了,我想你們經過這個流程和最終看到的結果應該是理解比較深入了的,至少我但願是這樣子的 ^_^,碼字是真累啊(估計你們看的也累- -!!)。

對了,除去這種jdk提供的動態代理方式,還有一種cglib的代理方式,這裏就不講了,你們能夠去了解下。

代理模式適用性

代理模式最典型的應用是AOP,關於AOP以後我會寫一份文章做些說明,而後代理模式在一些RPC框架中也應用普遍。

從一份java23種設計模式pdf文檔中摘抄一段代理模式適用性的描述:

1.遠程代理(RemoteProxy) 爲一個對象在不一樣的地址空間提供局部表明。
2.虛擬代理(VirtualProxy) 根據須要建立開銷很大的對象。
3.保護代理(ProtectionProxy) 控制對原始對象的訪問。
4.智能指引(SmartReference) 取代了簡單的指針,它在訪問對象時執行一 些附加操做。
複製代碼

最後我把文章中的源碼上傳到github了,地址以下,有問題歡迎交流:

github.com/ywp0919/Pro…

相關文章
相關標籤/搜索