AOP——JDK動態(tài)代理源碼解析

本篇將從源碼層面分析,JDK代理的具體實現(xiàn)方式。
摘錄源碼版本:JDK 1.8

概述

我們知道,在Spring AOP 中,創(chuàng)建代理有兩種方式,jdk動態(tài)代理與cglib動態(tài)代理。本篇先講一下JDK動態(tài)代理的低層原理。

JDK動態(tài)代理是使用了Java的反射機制,利用Proxy生成實現(xiàn)了目標(biāo)接口的代理類。

大綱:

  • JDK 動態(tài)代理實現(xiàn)步驟
  • JDK 動態(tài)代理使用方法
  • InvocationHander
  • Proxy.newProxyInstance
  • Proxy 生成代理類的源碼解析



JDK Proxy 實現(xiàn)原理

關(guān)鍵接口java.lang.reflect.InvocationHandler
關(guān)鍵類java.lang.reflect.Proxy

JDK動態(tài)代理實現(xiàn)步驟

JDK實現(xiàn)動態(tài)代理簡單來說有五個步驟:
1,創(chuàng)建自定義InvocationHander,通過繼承 InvocationHandler接口,來實現(xiàn)自定義的 invoke操作。(AOP邏輯就在 invoke() 方法中)
2,通過 Proxy.newProxyInstance 方法,生成并獲取代理類的實例對象。
3,在 Proxy.newProxyInstance 方法中,通過反射,獲取到代理類的構(gòu)造方法,方法簽名為 getConstructor(InvocationHandler.class)
4,通過構(gòu)造函數(shù)生成Proxy類的對象,并且生成時,將自定義的InvocationHandler實例對象作為參數(shù)傳入。
5,通過代理對象調(diào)用目標(biāo)方法。


JDK動態(tài)代理的使用

通過以下代碼,可以看到是如何利用 InvocationHanderProxy 生成代理對象并且使用代理的。

示例代碼如下:

// 自定義 InvocationHandler  (也可以看做是代理)
public class JDKProxy implements InvocationHandler {
    // 要代理的目標(biāo)實例
    private Object proxyObject;

    public JDKProxy(Object proxyObject) {
        this.proxyObject = proxyObject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // do something
        Object ret = null;  //方法返回值
        System.out.println("JDK Proxy --- 調(diào)用前 --- 調(diào)用方法是:"+method.getName() + "---參數(shù)是:"+ GsonUtil.toJson(args));
        ret = method.invoke(proxyObject, args); //對目標(biāo)實例調(diào)用invoke方法
        System.out.println("JDK Proxy --- 調(diào)用后");
        return ret;
    }
}

// 生成代理類的工廠方法
public static Object createJDKProxyInstance(Object proxyObject){
    JDKProxy jdkProxy = new JDKProxy(proxyObject);
    return Proxy.newProxyInstance(proxyObject.getClass().getClassLoader(), proxyObject.getClass().getInterfaces(), jdkProxy);
}

// 具體使用的代碼
DemoManager demoProxy = (DemoManager) ProxyFactory.createJDKProxyInstance(new DemoManagerCustom());


InvocationHandler

先來看看 InvocationHandler 這個接口
注釋是這么寫的

InvocationHandler is the interface implemented by the invocation handler of a proxy instance.
Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

InvocationHandler 是一個接口,每一個需要 自定義 方法調(diào)用處理器的 代理實例都要實現(xiàn)這個接口。
每一個代理實例都關(guān)聯(lián)著一個 invocation handler。當(dāng)代理實例的方法被調(diào)用的時候,這個方法就會編碼和轉(zhuǎn)發(fā)到 invocation handler的 invoke 方法調(diào)用。

這個接口只有一個方法:invoke()

public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

這個方法接受三個參數(shù):

  • proxy:指我們生成的代理對象實例
  • method:當(dāng)前調(diào)用的method對象
  • args:調(diào)用method的方法參數(shù)列表。


Proxy

Proxy類,就是用來創(chuàng)建動態(tài)代理對象的類。類注釋如下:

Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods

Proxy 提供了創(chuàng)建動態(tài)代理類和實例的靜態(tài)方法,同時這些動態(tài)代理類的超類也會在這些方法里被創(chuàng)建。


Proxy類提供了許多方法,動態(tài)代理用的方法是 newProxyInstance,使用方式在示例代碼中已經(jīng)給出。

我們可以看看源碼中方法的定義:

/**
  * 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
**/
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException

該方法接收三個參數(shù)

  • loader:一個ClassLoader對象,定義了由哪個ClassLoader來對生成的代理類進行加載。

  • interfaces:代理類的繼承接口數(shù)組,表示的是我需要給代理的類提供的接口,如果我提供了接口給這個代理類,那么這個代理對象就聲稱實現(xiàn)了這些接口,那么我們就可以調(diào)用這些接口的方法。
    JDK動態(tài)代理只能對實現(xiàn)了接口的目標(biāo)對象代理的根本原因

  • h:方法調(diào)用轉(zhuǎn)發(fā)的目的 invocation hander 實例。表示這個代理對象會關(guān)聯(lián)到哪個 invocation handler 上。


JDK動態(tài)代理原理

通過上面 InvocationHandler 和 Proxy 的介紹。那么動態(tài)代理的基本原理就可以捋清了:

1)通過繼承 InvocationHander,來自定義一個 invocation hander。這個hander 保存了一個代理目標(biāo)對象的引用。

2)在實現(xiàn) invoke() 方法中,我們把接收到的方法調(diào)用請求,用proxyObject來執(zhí)行。即 method.invoke(proxyObject, args),得到代理對象的方法調(diào)用結(jié)果并返回。

3)在 invoke 方法中嵌入 AOP 相關(guān)的處理邏輯。這樣就實現(xiàn)了目標(biāo)方法調(diào)用時,橫切邏輯的執(zhí)行。

4)代理對象的生成,使用 Proxy.newProxyInstance() 方法生成,傳入的三個參數(shù)依次為,目標(biāo)對象的類加載器,目標(biāo)對象實現(xiàn)的接口列表,invocation handler 實例。

5)這樣生成的代理對象,就是繼承了目標(biāo)對象接口列表的代理類的實例對象。
使用向上轉(zhuǎn)型的方式,把對象聲明為接口的實例。并且和目標(biāo)對象有一樣的方法。

6)利用多態(tài)的機制,對接口對象的方法調(diào)用,會動態(tài)鏈接到代理對象的方法調(diào)用,然后又會轉(zhuǎn)發(fā)到我們自定義 invocation hander 中,對 invoke 函數(shù)的調(diào)用,實現(xiàn)橫切邏輯的調(diào)用。



源碼解析

通過Debug模式查看代理對像的類型可以發(fā)現(xiàn)
Proxy.newProxyInstance() 生成的代理類名稱為 com.sun.proxy.$Proxy0。這個類是JVM在運行時動態(tài)生成的,以$開頭,Proxy為中間,最后的序號表示類對象的序號。
(生成代理類的邏輯可以參考 Proxy.ProxyClassFactory,下面放出了源碼)

其中有一個公共的構(gòu)造器:
public com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler),
那么可以知道,我們的代理實例對象,就是用這個構(gòu)造器生成的。
如圖:

代理類對象



Proxy.newProxyInstance


    /** parameter types of a proxy class constructor */
    private static final Class<?>[] constructorParams =
        { InvocationHandler.class };

    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);  //h 不能為空,所以 InvocationHandler 是必須的

        final Class<?>[] intfs = interfaces.clone();  //對象拷貝
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {  //校驗是否有權(quán)限創(chuàng)建一個代理類
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         * (尋找 或者 生成 指定的代理類)
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         * 調(diào)用這個代理類的構(gòu)造器,傳入指定的 invocation handler 對象
         */
        try {
            if (sm != null) {  //權(quán)限校驗,不細講
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            // 獲取參數(shù)是 InvocationHander.class 類型的構(gòu)造器
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            // 如果這個構(gòu)造器不是公有的,則把修飾符改為公有。
            if (!Modifier.isPublic(cl.getModifiers())) {  
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            // 調(diào)用構(gòu)造器,生成代理類的對象并且返回
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }



getProxyClass0
在上面方法中可以看到,生成動態(tài)代理類的方法,是調(diào)用了
getProxyClass0 方法。

而其中關(guān)鍵在于 proxyClassCache.get(loader, interfaces);
根據(jù)該方法的注釋可以明白,這里有一個代理類的緩存,如果這個代理類已經(jīng) 有指定類加載器并且繼承了這些接口,那么將直接返回生成的代理類,否則將由ProxyClassFactory生成代理類。

那么看看這個方法的源碼:

    /**
     * a cache of proxy classes(一個已經(jīng)生成了代理類的緩存)
     */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

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



ProxyClassFactory
創(chuàng)建類的核心還是在 ProxyClassFactory 類中。
可以看到 ProxyClassFactory 實現(xiàn)了函數(shù)式接口 BiFunction。(JDK8)
入?yún)⑹?類加載器(ClassLoader) 和 接口數(shù)組(Class<?>[])

    /**
     * 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<?>>
    {
        // prefix for all proxy class names 
        //(所有生成的代理類前綴都是 $Proxy)
        private static final String proxyClassNamePrefix = "$Proxy";

        // next number to use for generation of unique proxy class names
        // (代理類的生成序號)
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

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

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);

            // 遍歷接口數(shù)組,校驗:
            // 1)接口是否能找到,加載到的實例是否和傳入的接口實例一樣
            // 2)校驗加載的實例是否是一個接口
            // 3)接口去重
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                /*
                 * Verify that the Class object actually represents an
                 * interface.
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /*
                 * Verify that this interface is not a duplicate.
                 */
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }

            // 聲明代理類所在的包路徑
            String proxyPkg = null;     // package to define proxy class in
            // 代理類的訪問修飾符 
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            /*
             * 記錄非公有的接口包路徑,使得生成的代理類和這個接口包路徑相同
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * Choose a name for the proxy class to generate.
             * 生成代理類的名字
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * Generate the specified proxy class.
             * 生成類的字節(jié)碼,然后通過本地方法 defineClass0 生成代理類
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }
        }
    }



(如果有什么錯誤或者建議,歡迎留言指出)
(本文內(nèi)容是對各個知識點的轉(zhuǎn)載整理,用于個人技術(shù)沉淀,以及大家學(xué)習(xí)交流用)


參考資料:

深度剖析JDK動態(tài)代理機制(JDK Proxy源碼分析)
java動態(tài)代理機制詳解

靜態(tài)代理和動態(tài)代理的理解
Spring源碼剖析——AOP實現(xiàn)原理 (Spring 使用JDK Proxy )
SpringAOP兩種方式——JDKDynamicAopProxy和cglib2AopProxy源碼解析 (Spring 使用JDK Proxy )

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

相關(guān)閱讀更多精彩內(nèi)容

  • /Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home...
    光劍書架上的書閱讀 4,197評論 2 8
  • 本文主要講實現(xiàn)AOP的 代理模式原理,以及靜態(tài)代理,動態(tài)代理的區(qū)別和具體實現(xiàn)。 對SpringAOP的概念和使用,...
    _Zy閱讀 814評論 0 1
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,695評論 19 139
  • 很難說改變是件好事還是壞事,但是自從每天早上從禱告變成新聞之后,這個世界就焦慮了不少,因為讓每個人都處于惴惴不安的...
    WonderLAB閱讀 1,195評論 0 10
  • 荒野行動中玩家處處面臨各種抉擇,究竟是在城區(qū)降落還是郊外?究竟是隔岸觀火還是沖鋒陷陣?可能在其他游戲中玩家會因為“...
    陳小晨兒閱讀 494評論 0 0

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