淺析jdk動(dòng)態(tài)代理proxy的調(diào)用過(guò)程

通過(guò)這篇文章你會(huì)知道如下:

  • 動(dòng)態(tài)代理如何實(shí)現(xiàn)盖灸?
  • 代理對(duì)象與真實(shí)對(duì)象之間是什么關(guān)系旷太?
  • 通過(guò)代理對(duì)象的調(diào)用,invocationHandler中的invoke方法是如何被調(diào)用的

目前尚未厘清字節(jié)碼是如何生成,代理對(duì)象中的方法體是如何寫(xiě)入的?

動(dòng)態(tài)代理就是將代理插入到客戶和目標(biāo)之間妨托,從而為客戶和目標(biāo)對(duì)象之間引入一定的間接性发笔,這個(gè)間接性就可以給代理提供很多的活動(dòng)空間盟萨,代理可以在調(diào)用目標(biāo)對(duì)象的前后做些通知操作,從而實(shí)現(xiàn)新的功能或者擴(kuò)展目標(biāo)對(duì)象的功能了讨;

  • 動(dòng)態(tài)代理常規(guī)寫(xiě)法例子:
man--接口

boss--實(shí)現(xiàn)類(lèi)

代理

代理工廠--產(chǎn)生代理對(duì)象

測(cè)試
  • 調(diào)用過(guò)程分析

從上面的例子得知捻激,proxy是通過(guò)Proxy.newProxyInstance產(chǎn)生的,此時(shí)就有如下疑問(wèn):

  1. 調(diào)用proxy.doSomething時(shí)候前计,內(nèi)部是如何調(diào)用到invoke方法胞谭?
  2. 參數(shù)interface作用是啥?
    下面就開(kāi)始分析這些疑問(wèn)
    下面這張圖說(shuō)明男杈,代理對(duì)象是通過(guò)getProxyClass0方法產(chǎn)生的丈屹,最后通過(guò)newIntstance實(shí)例化,同時(shí)傳入invokehandler對(duì)象
    Proxy.newProxyInstance

下圖說(shuō)明代理對(duì)象的產(chǎn)生最終會(huì)落到proxyClassFactory中伶棒,注釋也有說(shuō)明這點(diǎn)旺垒,但是具體為什么會(huì)落到proxyClassFactory彩库,你打個(gè)斷點(diǎn)跟蹤會(huì),自然而然就知道了袖牙。所以現(xiàn)在的重點(diǎn)就在proxyClassFactory里面是如何產(chǎn)生對(duì)象的侧巨?

getProxyClass0方法

 private static final class ProxyClassFactory
    implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
    // prefix for all proxy class names
    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);
        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.
         */
        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());
        }
    }
}

proxyClassFactory類(lèi)中可以看到代理對(duì)象產(chǎn)生的名字構(gòu)成過(guò)程,同時(shí)會(huì)處理如果實(shí)現(xiàn)的接口訪問(wèn)權(quán)限不是public的情況鞭达;

String proxyName = proxyPkg + proxyClassNamePrefix + num;

生成代理對(duì)象的過(guò)程在這段代碼里面司忱,真正產(chǎn)生隊(duì)里對(duì)象的類(lèi)是ProxyGenerator.generateClassFile方法


代理對(duì)象產(chǎn)生

代理對(duì)象的方法構(gòu)建過(guò)程

最終生成的代理對(duì)象如下,可以發(fā)現(xiàn)在doSomething方法中畴蹭,發(fā)現(xiàn)是調(diào)用了h.invokerhandler的invoke方法坦仍,這個(gè)h對(duì)象則是父類(lèi)Proxy中的屬性,通過(guò)構(gòu)造方法進(jìn)行對(duì)象賦值叨襟;所以就可以解釋最初的疑問(wèn)了繁扎。

另外要獲取這個(gè)對(duì)象的反編譯的源碼,需要改變sun.misc.ProxyGenerator.saveGeneratedFiles參數(shù)糊闽,才能寫(xiě)入到硬盤(pán)梳玫,具體為什么是true,在ProxyGenerator.generateProxyClass方法中可以看到原因右犹。
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

package com.sun.proxy;

import com.example.proxy.Man;

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

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

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

public final boolean equals(Object paramObject)
        throws {
    try {
        return ((Boolean) this.h.invoke(this, m1, new Object[]{paramObject})).booleanValue();
    } catch (Error | RuntimeException localError) {
        throw localError;
    } catch (Throwable localThrowable) {
        throw new UndeclaredThrowableException(localThrowable);
    }
}

public final void doSomething()
        throws {
    try {
        this.h.invoke(this, m3, null);
        return;
    } catch (Error | RuntimeException localError) {
        throw localError;
    } catch (Throwable localThrowable) {
        throw new UndeclaredThrowableException(localThrowable);
    }
}

public final String toString()
        throws {
    try {
        return (String) this.h.invoke(this, m2, null);
    } catch (Error | RuntimeException localError) {
        throw localError;
    } catch (Throwable localThrowable) {
        throw new UndeclaredThrowableException(localThrowable);
    }
}

public final int hashCode()
        throws {
    try {
        return ((Integer) this.h.invoke(this, m0, null)).intValue();
    } catch (Error | RuntimeException localError) {
        throw localError;
    } catch (Throwable localThrowable) {
        throw new UndeclaredThrowableException(localThrowable);
    }
}

static {
    try {
        m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
        m3 = Class.forName("com.example.proxy.Man").getMethod("doSomething", new Class[0]);
        m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
        m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        return;
    } catch (NoSuchMethodException localNoSuchMethodException) {
        throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    } catch (ClassNotFoundException localClassNotFoundException) {
        throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
}

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末提澎,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子念链,更是在濱河造成了極大的恐慌盼忌,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件掂墓,死亡現(xiàn)場(chǎng)離奇詭異谦纱,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)君编,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)跨嘉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人吃嘿,你說(shuō)我怎么就攤上這事偿荷。” “怎么了唠椭?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵跳纳,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我贪嫂,道長(zhǎng)寺庄,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮斗塘,結(jié)果婚禮上赢织,老公的妹妹穿的比我還像新娘。我一直安慰自己馍盟,他們只是感情好于置,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著贞岭,像睡著了一般八毯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瞄桨,一...
    開(kāi)封第一講書(shū)人閱讀 51,488評(píng)論 1 302
  • 那天话速,我揣著相機(jī)與錄音,去河邊找鬼芯侥。 笑死泊交,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的柱查。 我是一名探鬼主播廓俭,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼唉工!你這毒婦竟也來(lái)了研乒?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤酵紫,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后错维,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體奖地,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年赋焕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了参歹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡隆判,死狀恐怖犬庇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情侨嘀,我是刑警寧澤臭挽,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站咬腕,受9級(jí)特大地震影響欢峰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一纽帖、第九天 我趴在偏房一處隱蔽的房頂上張望宠漩。 院中可真熱鬧,春花似錦懊直、人聲如沸扒吁。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)雕崩。三九已至,卻和暖如春波俄,著一層夾襖步出監(jiān)牢的瞬間晨逝,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工懦铺, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留捉貌,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓冬念,卻偏偏與公主長(zhǎng)得像趁窃,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子急前,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容