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

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

          public interface IHelloWorld{
          void sayHello();
          }


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


          這樣,我就可以創(chuàng)建一個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ù)時,我們都把這種調(diào)用請求攔截下來之后,做自己想做的事情。

           那怎么攔截呢?

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

           我們下面寫一個攔截器,讓他在函數(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");
          }
          }

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

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

           相應(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

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

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

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

           例如,假設(shè)我們希望讓源對象實(shí)現(xiàn)java.io.Closeable接口,則首先修改一下我們的Handler的invoke方法,讓他在獲取colse方法時,不要傳遞給源對象(因?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)容了。那如果我還想在裝配一個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 閱讀(515) 評論(0)  編輯  收藏

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


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

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

          常用鏈接

          留言簿(1)

          隨筆分類

          隨筆檔案

          文章分類

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 土默特左旗| 黑水县| 安塞县| 锦屏县| 泰和县| 绵竹市| 怀安县| 黑龙江省| 遵化市| 汾阳市| 手游| 岳普湖县| 浙江省| 肃宁县| 临高县| 二连浩特市| 马公市| 禄劝| 景德镇市| 敦煌市| 辽中县| 海门市| 内黄县| 茶陵县| 华阴市| 乌苏市| 通州区| 河间市| 永济市| 特克斯县| 饶阳县| 临江市| 广德县| 溧水县| 科技| 金阳县| 即墨市| 金门县| 泽库县| 玉林市| 清徐县|