天天看點

跟屌絲大哥學習設計模式---代理模式之動态代理

動态代理

java中動态代理機制的引入使得代理模式的思想更加完善與進步,它允許動态的建立代理并支援對動态的對所代理的方法進行調用。java動态代理類位于java.lang.reflect包下,一般主要涉及到以下兩個類: 

(1). interface invocationhandler:該接口中僅定義了一個方法object:invoke(object obj,method method, object[] args)。在實際使用時,第一個參數obj一般是指代理類,method是被代理的方法,如上例中的request(),args為該方法的參數數組。這個抽象方法在代理類中動态實作。 

(2).proxy:該類即為動态代理類,作用類似于上例中的proxysubject,其中主要包含以下内容: 

protected proxy(invocationhandler h):構造函數,估計用于給内部的h指派。 

static class getproxyclass (classloader loader, class[] interfaces):獲得一個代理類,其中loader是類裝載器,interfaces是真實類所擁有的全部接口的數組。 

static object newproxyinstance(classloader loader, class[] interfaces, invocationhandler h):傳回代理類的一個執行個體,傳回後的代理類可以當作被代理類使用(可使用被代理類的在subject接口中聲明過的方法)。 

所謂dynamic proxy是這樣一種class:它是在運作時生成的class,在生成它時你必須提供一組interface給它,然後該class就宣稱它實作了這些interface。你當然可以把該class的執行個體當作這些interface中的任何一個來用。當然啦,這個dynamic proxy其實就是一個proxy,它不會替你作實質性的工作,在生成它的執行個體時你必須提供一個handler,由它接管實際的工作。 下面我們通過動态代理來重新實作上面發送資訊的例子!

跟屌絲大哥學習設計模式---代理模式之動态代理

在上面的例子基礎上,我們先添加一個通過短信來發送消息的處理類:

public class smsmessage implements messagehandler {

@override

public void sendmessage(string msg) {

// todo auto-generated method stub

system.out.println("sms message :" + msg+" sent !");

}

//動态代理類

import java.lang.reflect.invocationhandler;

import java.lang.reflect.method;

public class dynamicmessageproxy implements invocationhandler {

private static int count;

private messagehandler msghandler;

public dynamicmessageproxy(messagehandler handler) {

msghandler = handler;

public object invoke(object proxy, method method, object[] args)

throws throwable {

system.out.println("++++++++=============+++++++++");

system.out.println("proxy:" + proxy.getclass());

system.out.println("method:" + method);

if (args != null && args.length == 1 && checkmessage((string) args[0])) {

count++;

system.out.println("message sent:" + count);

return method.invoke(msghandler, args);

return null;

private boolean checkmessage(string msg) {

return msg != null && msg.length() > 10;

//下面是調用

import java.lang.reflect.proxy;

public class mainclass {

private static void runproxy(messagehandler handler) {

handler.sendmessage("message for test");

/**

 * @param args

 */

public static void main(string[] args) {

// runproxy(new emailmessage());

// system.out.println("++++++++++++++++proxy++++++++++++++++++");

// runproxy(new messageproxy());

messagehandler handler = new emailmessage();

runproxy(handler);

messagehandler proxy = (messagehandler) proxy.newproxyinstance(

messagehandler.class.getclassloader(),

new class[] { messagehandler.class }, new dynamicmessageproxy(

handler));

runproxy(proxy);

system.out.println("++++++++++++++++++++++++++++++++++");

// 短信方式

handler = new smsmessage();

proxy = (messagehandler) proxy.newproxyinstance(messagehandler.class

.getclassloader(), new class[] { messagehandler.class },

new dynamicmessageproxy(handler));

下面為以上方法的輸出:

message for test send!!

++++++++=============+++++++++

proxy:class $proxy0

method:public abstract void messagehandler.sendmessage(java.lang.string)

message sent:1

++++++++++++++++++++++++++++++++++

sms message :message for test sent !

message sent:2

以上例子中,通過調用proxy.newproxyinstance方法建立動态代理對象,該方法需要傳入一個 類加載器、一組希望代理實作的接口清單、invocationhandler 接口的一個具體實作。動态代理可以将所有調用重定向到調用處理器,通常我們會向該處理器傳遞一個時間對象的引用。invoke()方法中傳遞進來了代理對象,當你需要區分請求來源時這是非常有用的,例如你可以通過判斷傳入的方法名屏蔽掉某些方法的執行!動态代理機制并不是會很頻繁使用的方法,它通常用來解決一些特定情況下的問題,是以不要盲目的為了使用而使用,要根據自己的實際需求來決定!

動态代理的應用十分廣泛,在struts2的攔截器中就用到了動态代理機制。還是以買車票為例子,現在用動态代理來實作,其中不懂的接口方法查查java api就會明白了,在此不多做解釋,代碼如下(注意passengerproxy類):

import java.lang.reflect.*;

public interface passenger {

public void buyticket();

public class realpassenger implements passenger {

public void buyticket() {

   // todo auto-generated method stub

   system.out.println("購買了車票");

// 用動态代理實作

/<code>/invocationhandler</code> 是代理執行個體的調用處理程式 實作的接口。 每個代理執行個體都具有一個關聯的調用處理程式。對代理執行個體調用方法時,将對方法調用進行編碼并将其指派到//它的調用處理程式的 <code>invoke</code> 方法。

public class passengerproxy implements invocationhandler {

public object obj;

// 把obj交給代理類

public object obj(object obj) {

   this.obj = obj;

   return proxy.newproxyinstance(obj.getclass().getclassloader(), obj

     .getclass().getinterfaces(), this);

    throws throwable {

   system.out.println("通過代理");

   method.invoke(obj, args);

   return null;

public class client {

   passengerproxy proxy = new passengerproxy();

   passenger passenger = (passenger) proxy.obj(new realpassenger());

   passenger.buyticket();

再來一個更牛的例子

/** 

 * 相親接口 

 *  

 * @author zhengt 

 * @time jun 3, 2095 3:13:03 pm 

 */  

public interface xiangqininterface {  

    /** 

     * 相親方法 

     */  

    public void xiangqin();  

}  

 * 張三相親實作類 

 * @time jun 3, 2095 3:14:48 pm 

public class zhangsanxiangqininterfaceimpl implements xiangqininterface {  

    public void xiangqin() {  

        system.out.println("張三去相親,娶個漂亮老婆。");  

    }  

import java.lang.reflect.invocationhandler;  

import java.lang.reflect.method;  

 * 相親可是一輩子的大事,相親前要準備一下,打扮得帥氣些。 

 * @time jun 3, 2095 3:15:48 pm 

public class readyinvocationhandler implements invocationhandler {  

    //相親接口的實作類,也就是張三相親類  

    private object zhangsan = null;  

    public readyinvocationhandler(object realsubject) {  

        this.zhangsan = realsubject;  

    public object invoke(object proxy, method m, object[] args) {  

        object result = null;  

        try {  

            /** 

             * 動态代理類$proxy0調用xiangqin方法時會調用它自己的xiangqin方法, 

             * 而它自己的xiangqin方法裡面調用的是super.h.invoke(this, , ),也就是父類proxy的h的invoke方法, 

             * 也就是readyinvocationhandler類的invoke方法。 

             * 是以,invoke(object proxy, method m, object[] args)種的proxy實際上就是動态代理類$proxy0, 

             * 如果你将其強轉成xiangqininterface然後調用它的xiangqin方法,然後它就會調用super.h.invoke(this, , ),這樣就會死循環。 

             */  

             * 網上關于這裡最多問題就是object proxy放在這裡用來做什麼呢?這個我也不知道, 

             * 不過至少我們知道它到底是個什麼東西,具體做什麼用嘛就不得而知了 

            system.out.println(proxy.getclass().getsimplename());  

            system.out.println("張三相親前,代理人給他打扮了打扮。");  

            result = m.invoke(zhangsan, args);  

        } catch (exception ex) {  

            system.exit(1);  

        }  

        return result;  

import java.lang.reflect.proxy;  

 * 張三來到了婚介所(相親現場),開始相親。 

 * @time jun 3, 2095 3:17:16 pm 

public class hunjiesuo {  

    public static void main(string args[]) {  

        //先将張三相親這個相親的實作類執行個體化,也就是得到xiangqininterface接口的一個執行個體對象  

        xiangqininterface zhangsan = new zhangsanxiangqininterfaceimpl();  

        /** 

         * 得到zhangsanxiangqininterfaceimpl這個類的一個代理類,同時為代理類綁定了一個處理類readyinvocationhandler。 

         * 聽着很繞口,其實就是每次調用zhangsanxiangqininterfaceimpl這個子類的xiangqin方法時, 

         * 不是zhangsan這個zhangsanxiangqininterfaceimpl類的執行個體去調用, 

         * 而是這個zhangsanxiangqininterfaceimpl的代理類readyinvocationhandler去調用它自己的invoke方法, 

         * 這個invoke方法裡呢可以調用zhangsan這個執行個體的xiangqin方法 

         */  

         * 在java種怎樣實作動态代理呢 

         * 第一步,我們要有一個接口,還要有一個接口的實作類,而這個實作類呢就是我們要代理的對象, 

         * 所謂代理呢也就是在調用實作類的方法時,可以在方法執行前後做額外的工作,這個就是代理。 

         * 第二步,我們要自己寫一個在要代理類的方法執行時,能夠做額外工作的類,而這個類必須繼承invocationhandler接口, 

         * 為什麼要繼承它呢?因為代理類的執行個體在調用實作類的方法的時候,不會調真正的實作類的這個方法, 

         * 而是轉而調用這個類的invoke方法(繼承時必須實作的方法),在這個方法中你可以調用真正的實作類的這個方法。 

         * 第三步,在要用代理類的執行個體去調用實作類的方法的時候,寫出下面兩段代碼。 

        xiangqininterface proxy = (xiangqininterface) proxy.newproxyinstance(  

                zhangsan.getclass().getclassloader(),  

                zhangsan.getclass().getinterfaces(),  

                new readyinvocationhandler(zhangsan));  

        proxy.xiangqin();  

         * 這裡要解釋下中部那段長長的代碼的意思,以及具體做了哪些工作? 

         * 第一,根據zhangsan.getclass().getclassloader()這個要代理類的類加載器和 

         * zhangsan.getclass().getinterfaces()要代理類所實作的所有的接口 

         * 作為參數調用proxy.getproxyclass(classloader loader, class&lt;?&gt;... interfaces) 

         * 的方法傳回代理類的java.lang.class對象,也就是得到了java動态生成的代理類$proxy0的class對象。 

         * 同時,java還讓這個動态生成的$proxy0類實作了要代理類的實作的所有接口,并繼承了proxy接口。 

         * 第二,執行個體化這個動态生成的$proxy0類的一個執行個體,執行個體化代理類的構造函數為proxy(invocationhandler h), 

         * 也就是說要執行個體化這個動态生成的$proxy0類,必須給它一個invocationhandler參數,也就是我們自己實作的用來在代理類 

         * 方法執行前後做額外工作的類readyinvocationhandler。 

         * 這 段代碼 proxy.newproxyinstance(zhangsan.getclass().getclassloader(),zhangsan.getclass().getinterfaces(),new readyinvocationhandler(zhangsan)) 

         * 得到的其實是一個類名叫$proxy0 extends proxy implements xiangqininterface的類。 

         * 第三,将這個$proxy0類強制轉型成xiangqininterface類型,調用xiangqin方法。 

網上看到一個牛人讀過這個動态代理的源代碼,現把網址留在這裡,希望對網友們有幫助。 

<a target="_blank" href="http://hi.baidu.com/malecu/blog/item/45d4952b31bc0e27d52af17a.html">http://hi.baidu.com/malecu/blog/item/45d4952b31bc0e27d52af17a.html</a>

在目前的java開發包中包含了對動态代理的支援,但是其實作隻支援對接口的的實作。 

其實作主要通過是java.lang.reflect.proxy類和java.lang.reflect.invocationhandler接口。 

proxy類主要用來擷取動态代理對象,invocationhandler接口用來限制調用者實作,如下,diaosi接口定義的 業務方法,diaosinanimpl是diaosi接口的實作,

diaosinanhandler是 invocationhandler接口實作。代碼如下:

業務接口:

package proxy;

public interface diaosi {

void luguan();

 業務接口實作:

public class diaosinan implements diaosi {

    @override

    public void luguan() {

        // todo auto-generated method stub

        system.out.println("我是撸管王");

    }

 invocationhandler實作,需要在接口方法調用前後加入一部份處理工作,這裡僅僅在方法調用前後向背景輸出兩句字元串,其代碼如下:

public class diaosinanhander implements invocationhandler{

    //要代理的原始對象 

private object diaosinan; 

* 構造函數。

* @param obj 要代理的原始對象。

*/ 

public diaosinanhander(object diaosinan) {

    this.diaosinan = diaosinan;

    public object invoke(object proxy, method method, object[] args)

            throws throwable {

        object result ;

        dobefore(); 

        //調用原始對象的方法 

        result = method.invoke(this.diaosinan ,args); 

        //方法調用之後 

        doafter();

        return result;

    private void doafter() {

system.out.println("after method invoke!"); 

    private void dobefore() {

        system.out.println("before method invoke!"); 

測試代碼:

public class test {

diaosi ds = new diaosinan();

diaosinanhander handler = new diaosinanhander(ds);

diaosi proxy = (diaosi) proxy.newproxyinstance(

ds.getclass().getclassloader(),

ds.getclass().getinterfaces(),

handler);

/*public static object newproxyinstance(classloader loader,

        class&lt;?&gt;[] interfaces,

        invocationhandler h)

throws illegalargumentexception

{

if (h == null) {

throw new nullpointerexception();

}*/

proxy.luguan();

Ø 首先擷取一個業務接口的實作對象; 

Ø 擷取一個invocationhandler實作,此處是

diaosinanhanderhandler對象;

Ø 建立動态代理對象; 

Ø 通過動态代理對象調用luguan()方法,此時會在原始對象diaosinan.luguan()方法前後輸出兩句字元串。 

運作測試類輸出如下: 

before method invoke!

我試撸管王

after method invoke!

此處test類中的方法調用代碼比較多,在我們的實際應用中可以通過配置檔案來來簡化用戶端的調用實作。另外也可以通過動态代理來實作簡單的aop。

====================================分割線================================

最新内容請見作者的github頁:http://qaseven.github.io/