<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 13,  comments - 3,  trackbacks - 0
    最近在學(xué)習(xí)Spring。某大人跟我說,Spring的AOP其實(shí)就是Java反射中的動(dòng)態(tài)代理。OK,那我就從動(dòng)態(tài)代理開始看起。

    一、基本概念
    所謂動(dòng)態(tài)代理,基本上是如下場景:假設(shè)我有個(gè)接口IHelloWorld

    public interface IHelloWorld{
    void sayHello();
    }


    我再有一個(gè)實(shí)現(xiàn)類HelloWorldImpl實(shí)現(xiàn)了IHelloWorld接口
    public class HelloWorldImpl implements IHelloWorld{
    public void sayHello(){
    System.out.println(
    "Hello, World");
    }
    }


    這樣,我就可以創(chuàng)建一個(gè)HelloWorldImpl對象,來實(shí)現(xiàn)IHelloWorld中定義的服務(wù)。

     問題是,現(xiàn)在,我打算為HelloWorldImpl增強(qiáng)功能,需要在調(diào)用sayHello方法前后各執(zhí)行一些操作。在有些情況下,你無法修改HelloWorldImpl的源代碼,那怎么辦呢?
    從道理上來說,我們可以攔截對HelloWorldImpl對象里sayHello()函數(shù)的調(diào)用。也就是說,每當(dāng)有代碼調(diào)用sayHello函數(shù)時(shí),我們都把這種調(diào)用請求攔截下來之后,做自己想做的事情。

     那怎么攔截呢?

     首先,需要開發(fā)一個(gè)InvocationHandler。這個(gè)東東表示的是,你攔截下函數(shù)調(diào)用之后,究竟想干什么。InvocationHandler是一個(gè)接口,里面的聲明的函數(shù)只有一個(gè):
    Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    這個(gè)函數(shù)表示一次被攔截的函數(shù)調(diào)用。因此,proxy表示這個(gè)被攔截的調(diào)用,原本是對哪個(gè)對象調(diào)用的;method表示這個(gè)被攔截的調(diào)用,究竟是調(diào)用什么方法;args表示這個(gè)被攔截的調(diào)用里,參數(shù)分別是什么。

     我們下面寫一個(gè)攔截器,讓他在函數(shù)調(diào)用之前和之后分別輸出一句話。

    import java.lang.reflect.*;

    public class HelloHandler implements InvocationHandler{
    Object oriObj;

    public HelloProxy(Object obj){
    oriObj 
    = obj;
    }

    public Object invoke(Object proxy, Method m, Object[] args) throws Throwable{
    Object result 
    = null;

    //在函數(shù)調(diào)用前輸出一些信息
        System.out.println("################################");
    String methodName 
    = m.getName();
    System.out.println(
    "method name : " + methodName);
    doBefore();

    //利用反射,進(jìn)行真正的調(diào)用
        result = m.invoke(oriObj, args);

    //在函數(shù)調(diào)用后執(zhí)行
        doAfter();
    System.out.println(
    "################################");
    return result;
    }

    public void doBefore(){
    System.out.println(
    "Do Before");
    }
    public void doAfter(){
    System.out.println(
    "Do After");
    }
    }

     有了這個(gè)Handler之后,下面要做的,就是把這個(gè)Handler和一個(gè)IHelloWorld類型的對象裝配起來。重點(diǎn)的函數(shù)只有一個(gè),那就是java.lang.reflect.Proxy類中的一個(gè)靜態(tài)工廠方法:
    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
    這個(gè)方法返回一個(gè)對象,我們稱返回的對象為代理對象(proxy)。
    而后,我們就不把真正的原對象暴露給外接,而使用這個(gè)代理對象。這個(gè)代理對象接受對源對象的一切函數(shù)調(diào)用(也就是把所有調(diào)用都攔截了),然后根據(jù)我們寫的InvocationHandler,來對函數(shù)進(jìn)行處理。

     產(chǎn)生代理對象的過程,我把它理解成一個(gè)裝配的過程:由源對象、源對象實(shí)現(xiàn)的接口、InvocationHandler裝配產(chǎn)生一個(gè)代理對象。

     相應(yīng)的測試代碼如下:

    public class TestHello{
    public static void main(String args[])throws Exception{
    HelloWorldImpl h 
    = new HelloWorldImpl();

    Object proxy 
    = Proxy.newProxyInstance(
    h.getClass().getClassLoader(),
    new Class[]{IHelloWorld.class},
    new HelloProxy(h)
    );
    ((IHelloWorld)proxy).sayHello();
    }
    }

     利用ant編譯運(yùn)行的結(jié)果:
    [java] ################################
    [java] method name : sayHello
    [java] Do Before
    [java] Hello, World
    [java] Do After
    [java] ################################


    二、更多理解
    我們看產(chǎn)生代理對象的newProxyInstance函數(shù)的聲明:
    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

     這個(gè)函數(shù)的第一個(gè)參數(shù)是ClassLoader,第三個(gè)參數(shù)是InvocationHandler,基本都沒什么問題。
    第二個(gè)參數(shù)是一個(gè)Class類型的數(shù)組,名字叫interfaces,表示的是產(chǎn)生的動(dòng)態(tài)代理對象實(shí)現(xiàn)的接口。

     仔細(xì)想想,有兩個(gè)問題。第一,產(chǎn)生一個(gè)代理對象,需要源對象么?第二,我能不能產(chǎn)生一個(gè)動(dòng)態(tài)代理對象,來實(shí)現(xiàn)源對象沒有實(shí)現(xiàn)的接口?

     第一個(gè)問題和第二個(gè)問題其實(shí)是一致的。我們完全可以脫離源對象,而直接產(chǎn)生一個(gè)代理對象,也可以利用動(dòng)態(tài)代理,讓源對象實(shí)現(xiàn)更多的接口,為源對象增強(qiáng)功能。

     例如,假設(shè)我們希望讓源對象實(shí)現(xiàn)java.io.Closeable接口,則首先修改一下我們的Handler的invoke方法,讓他在獲取colse方法時(shí),不要傳遞給源對象(因?yàn)樵磳ο鬀]有實(shí)現(xiàn)該方法):

    public Object invoke(Object proxy, Method m, Object[] args) throws Throwable{
    Object result 
    = null;

    //在函數(shù)調(diào)用前輸出一些信息
      System.out.println("################################");
    String methodName 
    = m.getName();
    System.out.println(
    "method name : " + methodName);
    doBefore();

    //判斷是否是Closeabled的方法
      if (m.getDeclaringClass().isAssignableFrom(java.io.Closeable.class)){
    System.out.println(
    "I got the close() method!");
    }
    else{
    //傳遞給源對象
    //利用反射,進(jìn)行真正的調(diào)用
        result = m.invoke(oriObj, args);
    }

    //在函數(shù)調(diào)用后執(zhí)行
      doAfter();
    System.out.println(
    "################################");
    return result;
    }

     然后,我們在裝配的過程中,改變一下參數(shù),并強(qiáng)轉(zhuǎn)之后調(diào)用一下close方法:

    Object proxy = Proxy.newProxyInstance(
    h.getClass().getClassLoader(),
    new Class[]{IHelloWorld.class,java.io.Closeable.class},
    new HelloProxy(h)
    );
    ((Closeable)proxy).close();

     ant運(yùn)行結(jié)果:
    [java] ################################
    [java] method name : close
    [java] Do Before
    [java] I got the close() method!
    [java] Do After
    [java] ################################

    三、更多的代理~
    我們現(xiàn)在能夠讓sayHello()函數(shù)執(zhí)行之前和之后,輸出一些內(nèi)容了。那如果我還想在裝配一個(gè)Handler呢?
    最簡單的方法:

    Object proxy = Proxy.newProxyInstance(
    h.getClass().getClassLoader(),
    new Class[]{IHelloWorld.class, java.io.Closeable.class},
    new HelloProxy(h)
    );
    Object proxy2 
    = Proxy.newProxyInstance(
    h.getClass().getClassLoader(),
    new Class[]{IHelloWorld.class, java.io.Closeable.class},
    new HelloProxy(proxy)
    );
    ((IHelloWorld)proxy2).sayHello();

    ant運(yùn)行結(jié)果:
    [java] ################################
    [java] method name : sayHello
    [java] Do Before
    [java] ################################
    [java] method name : sayHello
    [java] Do Before
    [java] Hello, World
    [java] Do After
    [java] ################################
    [java] Do After
    [java] ################################

    不用我解釋了吧!

    posted on 2009-03-02 22:28 Antony Lee 閱讀(519) 評論(0)  編輯  收藏

    只有注冊用戶登錄后才能發(fā)表評論。


    網(wǎng)站導(dǎo)航:
     

    <2009年3月>
    22232425262728
    1234567
    891011121314
    15161718192021
    22232425262728
    2930311234

    常用鏈接

    留言簿(1)

    隨筆分類

    隨筆檔案

    文章分類

    搜索

    •  

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 国产福利视精品永久免费 | 亚洲国产美女视频| 免费看一区二区三区四区| 亚洲国产精品狼友中文久久久| 亚洲啪AV永久无码精品放毛片 | 国产AV无码专区亚洲AV麻豆丫| 99热在线精品免费全部my| 亚洲第一页在线播放| 222www在线观看免费| 亚洲youjizz| 免费特级黄毛片在线成人观看| 亚洲精品女同中文字幕| 国产精品美女自在线观看免费| 国产精品亚洲а∨天堂2021| 免费一级一片一毛片| 国产99视频精品免费视频76| 亚洲精品无码久久久久去q| 久久免费观看国产精品88av| 亚洲视频在线观看地址| 日韩精品无码区免费专区| 久久亚洲中文无码咪咪爱| 亚洲AV网站在线观看| 久久精品无码专区免费| 日产亚洲一区二区三区| 国产免费不卡v片在线观看| 亚洲国产午夜精品理论片在线播放| 成人爱做日本视频免费| 国产免费伦精品一区二区三区 | 久久久久久久亚洲Av无码| 国产免费久久精品99re丫y| 国产精品亚洲综合一区在线观看| 国产日产亚洲系列最新| 1000部无遮挡拍拍拍免费视频观看 | 国产精品无码免费视频二三区| 在线观看亚洲AV日韩A∨| 全亚洲最新黄色特级网站| 在线观看黄片免费入口不卡| 亚洲一区二区三区无码国产 | 久久成人免费大片| 亚洲中文无码永久免费| 中文字幕第13亚洲另类|