Java--動態(tài)代理

首先先理解一下為什么要使用代理丛晦。在日常生活中,代理可以解決業(yè)務(wù)相關(guān)雙方直接交流的不便的問題提陶,同時還可以提供比直接交流更多的功能烫沙,而在編程領(lǐng)域,代理類一般是做些除原始類核心功能以外的其他功能隙笆,比如修改權(quán)限等需要專門的代理來實現(xiàn)锌蓄。代碼每個類代表一個主要功能,而不是將所有功能混在一個類中撑柔,這樣的代碼清晰有條理瘸爽,易于維護,如果要修改權(quán)限铅忿,不必修改原始類代碼剪决,直接修改權(quán)限代理類就可以了。這樣不需要修改權(quán)限的代碼可以還用原始類檀训,需要的柑潦,就調(diào)用代理類,不會影響原來的功能峻凫。
代理類提供一個與原始相同的接口渗鬼,以便可以在任何時候替代原始。代理類通常在客戶端調(diào)用傳遞給原始類之前或之后荧琼,執(zhí)行某個操作譬胎,而不是單純地將調(diào)用傳遞給原始類,同時命锄,代理類可以在執(zhí)行原始類操作時堰乔,附加其他的操作,相當(dāng)于對原始類進(jìn)行封裝累舷。
而動態(tài)代理的作用是浩考,只要建立一個動態(tài)代理類,就可以為多個原始類進(jìn)行代理被盈,解決了靜態(tài)代理的問題(一個原始類對應(yīng)一個靜態(tài)代理析孽,有多少原始類就有多少靜態(tài)代理,造成代碼瑣碎)只怎。
在java的動態(tài)代理機制中袜瞬,有一個重要的接口 InvocationHandler(Interface)和一個重要的類 Proxy(Class),這兩個是實現(xiàn)動態(tài)代理所必須用到的身堡。每一個動態(tài)代理類都必須要實現(xiàn)InvocationHandler這個接口邓尤,并且每個代理類的實例都關(guān)聯(lián)到了一個handler,當(dāng)我們通過代理對象調(diào)用一個方法的時候,這個方法的調(diào)用就會被轉(zhuǎn)發(fā)為由InvocationHandler這個接口的 invoke 方法來進(jìn)行調(diào)用

* @param proxy 所代理的真實對象
     *            the proxy instance on which the method was invoked
     * @param method 所要調(diào)用真實對象的某個方法的Method對象
     *            the method invoked on the proxy instance
     * @param args 調(diào)用真實對象某個方法時接受的參數(shù)
     *            an array of objects containing the parameters passed to the
     *            method, or {@code null} if no arguments are expected.
     *            Primitive types are boxed.
     *
     * @return the result of executing the method. Primitive types are boxed.
     *
     * @throws Throwable
     *             the exception to throw from the invoked method on the proxy.
     *             The exception must match one of the declared exception types
     *             of the invoked method or any unchecked exception type. If not
     *             then an {@code UndeclaredThrowableException} is thrown
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

再來看看Proxy類汞扎,它的作用是用來動態(tài)創(chuàng)建一個代理對象的類季稳,它提供了許多的方法,但是用的最多的就是 newProxyInstance 這個方法

* @param loader 一個ClassLoader對象澈魄,
                            定義了由哪個ClassLoader對象來對生成的代理對象進(jìn)行加載
     *            the class loader that will define the proxy class
     * @param interfaces 一個Interface對象的數(shù)組景鼠,
                                表示的是將要給需要代理的對象提供一組什么接口,
                                如果提供了一組接口給它痹扇,
                                那么這個代理對象就宣稱實現(xiàn)了該接口(多態(tài))铛漓,
                                這樣就能調(diào)用這組接口中的方法了
     *            an array of {@code Class} objects, each one identifying an
     *            interface that will be implemented by the returned proxy
     *            object
     * @param invocationHandler 一個InvocationHandler對象,
                                        表示的是當(dāng)這個動態(tài)代理對象在調(diào)用方法的時候鲫构,
                                        會關(guān)聯(lián)到哪一個InvocationHandler對象上
     *            the invocation handler that handles the dispatched method
     *            invocations
     * @return a new proxy object that delegates to the handler {@code h}
     * @throws IllegalArgumentException
     *                if any of the interface restrictions are violated
     * @throws NullPointerException
     *                if the interfaces or any of its elements are null
     */
    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
                                          InvocationHandler invocationHandler)
            throws IllegalArgumentException {

之后看一個demo DynamicProxy20170607來看看動態(tài)代理如何使用浓恶。
最后結(jié)果:


Paste_Image.png

“$ProxyN”是代理類名稱,由


Paste_Image.png

打印出的结笨,其中 N 是一個逐一遞增的阿拉伯?dāng)?shù)字包晰,代表 Proxy 類第 N 次生成的動態(tài)代理類,但不是每次調(diào)用 Proxy 的靜態(tài)方法創(chuàng)建動態(tài)代理類都會使得 N 值增加禀梳,因為如果對同一組接口(包括接口排列的順序相同)試圖重復(fù)創(chuàng)建動態(tài)代理類杜窄,它會返回先前已經(jīng)創(chuàng)建好的代理類的類對象,而不會再嘗試去創(chuàng)建一個全新的代理類算途,這樣可以節(jié)省不必要的代碼重復(fù)生成塞耕,提高了代理類的創(chuàng)建效率。
通過 Proxy.newProxyInstance 創(chuàng)建的代理對象是在jvm運行時動態(tài)生成的一個對象嘴瓤,它并不是InvocationHandler類型扫外,也不是定義的那組接口的類型,而是在運行是動態(tài)生成的一個對象廓脆。
之后調(diào)用
Paste_Image.png

通過代理對象來調(diào)用接口中的方法筛谚,程序就會跳轉(zhuǎn)到由這個代理對象關(guān)聯(lián)到的 handler 中的invoke方法去執(zhí)行,而這個 handler 對象又接受了一個 helloImpl類型的參數(shù)


Paste_Image.png

表示要代理的就是這個helloImpl真實對象停忿,所以此時就會調(diào)用 handler(DynamicProxy) 中的invoke方法去執(zhí)行驾讲,可以看到DynamicProxy中的invoke不僅調(diào)用了helloImpl這個真實對象(helloImpl實現(xiàn)了Hello接口),還在前后添加了一些操作席赂,另外通過
Paste_Image.png

可以看到具體調(diào)用了什么方法吮铭,
結(jié)果中before say hello,和after say hello都是添加的操作颅停,從Method:public abstract void DynamicProxy20170607.Hello.helloCat()可以看到谓晌,確實是調(diào)用的Hello接口helloCat的實現(xiàn)方法。
Paste_Image.png

這也就證明了當(dāng)通過代理對象來調(diào)用方法的時候癞揉,起實際就是委托由其關(guān)聯(lián)到的 handler 對象的invoke方法中來調(diào)用纸肉,并不是自己來真實調(diào)用溺欧,而是通過代理的方式來調(diào)用的。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末柏肪,一起剝皮案震驚了整個濱河市姐刁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌烦味,老刑警劉巖龙填,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異拐叉,居然都是意外死亡,警方通過查閱死者的電腦和手機扇商,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進(jìn)店門凤瘦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人案铺,你說我怎么就攤上這事蔬芥。” “怎么了控汉?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵笔诵,是天一觀的道長。 經(jīng)常有香客問我姑子,道長乎婿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任街佑,我火速辦了婚禮谢翎,結(jié)果婚禮上沐旨,老公的妹妹穿的比我還像新娘磁携。我一直安慰自己闷供,他們只是感情好这吻,可當(dāng)我...
    茶點故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著香璃,像睡著了一般葡秒。 火紅的嫁衣襯著肌膚如雪赖草。 梳的紋絲不亂的頭發(fā)上秧骑,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天,我揣著相機與錄音,去河邊找鬼士修。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的雹锣。 我是一名探鬼主播,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起滥搭,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎媒役,沒想到半個月后次泽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體啊片,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡玖像,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年紫谷,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捐寥。...
    茶點故事閱讀 39,773評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡笤昨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出握恳,到底是詐尸還是另有隱情瞒窒,我是刑警寧澤,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布乡洼,位于F島的核電站崇裁,受9級特大地震影響匕坯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜寇壳,卻給世界環(huán)境...
    茶點故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一醒颖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧壳炎,春花似錦泞歉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至铲球,卻和暖如春挺庞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背稼病。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工选侨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人然走。 一個月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓援制,卻偏偏與公主長得像,于是被迫代替她去往敵國和親芍瑞。 傳聞我的和親對象是個殘疾皇子晨仑,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,689評論 2 354

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

  • Java動態(tài)代理 引言 最近在看AOP代碼,其中利用到了Java動態(tài)代理機制來實現(xiàn)AOP織入拆檬。所以好好地把Java...
    草捏子閱讀 1,534評論 0 18
  • 前言 本文是我在學(xué)習(xí)代理模式時的一篇筆記洪己,除了對代理模式、靜態(tài)和動態(tài)代理的概念和實現(xiàn)進(jìn)行了描述外竟贯,還有關(guān)于動態(tài)代理...
  • 之前介紹的反射和注解都是Java中的動態(tài)特性答捕,還有即將介紹的動態(tài)代理也是Java中的一個動態(tài)特性。這些動態(tài)特性使得...
    Single_YAM閱讀 2,051評論 0 9
  • 基礎(chǔ):class文件簡介及加載流程 Java編譯器編譯好Java文件之后屑那,產(chǎn)生.class 文件在磁盤中噪珊。這種cl...
    jiangmo閱讀 478評論 0 1
  • 好不容易把書整理好,她趴在桌上齐莲,趴著趴著痢站,她閉上了眼睛… 她便是此文的女主人公:蕾,她一直以來都是按照自...
    魚小霧閱讀 233評論 0 0