seaairland

           

          Dynamic Proxy介紹

          一、 proxy模式簡介
          GoF介紹了proxy模式。代理對象為其他對象提供一種代理以控制對這個對象的訪問。它靜態結構如下:
          Client 需要訪問 RealSubject 時,它實際訪問的是 Proxy 對象,而后 Proxy 對象將請求委托給 RealSubject RealSubject 實現了主要的邏輯, Proxy 對象可以在處理請求之前、之后作額外的處理。可以看出, Proxy RealSubject 實現了同樣的接口,這樣 Client 才可以調用 RealSubject 實現的所有 Subject 的方法。
          我們在實現 Proxy 時,如果使用的是 C++ 語言,我們可以重載操作符 -> 來實現代理。優點是實現簡單,缺點是它不能區別對待不同的請求。當然也可以是用普通的形式,創建一個代理類,實現接口,并將調用委托給被代理的對象。
          如果使用的是 Java 語言,我們當然可以使用普通的形式來實現 Proxy 模式。但是 JDK1.3 引入了 dynamic proxy ,它允許我們更容易的實現代理。
          二、 JDK中的Dynamic Proxy介紹
          它由 java.lang.reflect.Proxy java.lang.reflect.InvocationHandler 等組成。 Proxy 類擁有一個 protected InvocationHandler 類型的成員變量。 它只能代理 Interface。
          它的基本思想如下:
          1.???? 代理類由 Proxy 的靜態方法 getProxyClass 來動態的創建出來。該方法所需要的一個參數為它所代理的接口數組。創建出來的代理類實現了所有的接口,并且繼承了 Proxy
          2.???? 代理類實現的接口方法的處理邏輯為,調用父類的 InvocationHandler 類型的成員變量的 invoke 方法。
          由此可以看出,必須讓 Proxy 對象擁有一個正確的 InvocationHandler 的實現。 Proxy 對象由 Proxy 的靜態成員函數 newProxyInstance 來創建,該函數的最后一個參數為 InvocationHandler 類型。動態生成的代理類實現的所有接口方法都被委托給 InvocationHandler 接口的 invoke 方法。
          三、 例子代碼
          假如有如下接口:
          interface Foo {
          ?void f(String s);
          ?void g(int i);
          ?String h(int i, String s);
          }
          并且有一個實現
          class Implement implements Foo {
          ?? …
          }
          現在我們想在 Foo 接口的每個方法調用時,加入日至。一個很簡單很直觀的方法如下:
          class LogProxy implements Foo {
          private Foo delegate ;
          ?? public LogProxy( Foo foo ) {
          ????????? delegate = foo ;
          }
          public String h(int i, String s) {
          ????? System.out.println(“call h begin ”) ;
          ????? String result = delegate.h( i , s ) ;
          ????? System.out.println(“call h end ”) ;
          ????? Return result ;
          }
          }
          new LogProxy( new Implement()   ).h( 10 , “str”);
          可以看出這樣的實現代碼很多,而且幾乎都是相同的。當然可以編寫程序來寫出這樣的代碼,但是我們如果使用 JDK1.3 引入的 dynamic proxy ,那么情況就完全不同了。
          四、 dynamic proxy實現Log的代碼
          1.?????? 編寫 InvocationHandler 的子類,來攔截所有的方法調用。
          class LogProxy implements InvocationHandler {
          ????? public LogProxy( Object object ) { obj = object ; }
          ????? public Object invoke(Object proxy, Method method, Object[] args)
          ????? throws Throwable {??
          String methodName = method.getName() ;
          System.out.println("call " + methodName?+ “ begin “ ) ;???????
          ?????? ??? Object result = method.invoke( obj , args )?;
          System.out.println("call " + methodName + “ end“ ) ;???????
          ??? ?????? Return result ;
          ????? }
          ????? public Object obj = null ;
          }
          2.?????? 使用 Proxy的靜態方法來創建代理類。?
          LogProxy dp = new LogProxy( new Implment() ) ;
          Foo proxy = (Foo) Proxy.newProxyInstance(
          ??????????? ????????// [1] class loader
          ????? ????????????? Foo.class.getClassLoader(),
          ?????????????????? // [2] interface's Class array
          ?????? ?????????????????? new Class[]{ Foo.class },
          ?????? ?????????????????? // [3] InvocationHandler
          ?????????????????? dp ) ;
          3.?????? 客戶在代理類上調用方法
          proxy. h( 10 , “str”);
          可以看出,如果接口中有很多方法,那么使用 dynamic proxy 是很合適的,但是如果接口只有很少的方法,可能使用普通的方法更直觀,也更簡單。
          五、 應用例子
          如果我們設計一個數據庫連接池,接口如下:
          interface DBConnectionPool {
          ??????????? public java.sql.Connection getConnection() ;
          ??????????? public void releaseConnection( java.sql.Connection conn ) ;
          }
          class DBConnectionPoolImpl implements DBConnectionPool {
          ??????????? …
          }
          ?
          那么一個可能的用戶調用序列如下:
          void getData() {
          ??????????? DBConnectionPoolImpl?cpi = new DBConnectionPoolImpl() ;
          ??????????? Connection conn = cpi.getConnection() ;
          ??????????? // use conn to retrieve data from db
          ??????????? …
          ??????????? cpi. releaseConnection( conn ) ;
          ???????????
          }
          藍色的代碼表示了將連接還給連接池,因為所有的連接都是由連接池來管理的。但是這樣的代碼對用戶來講可能不太習慣,而且迫使用戶這樣編寫代碼,用戶會意識到 cpi 對連接作了特殊的處理。
          ??????????? 一個更好的方法是調用 Connection 接口的 close 方法。這樣的代碼如下:
          void getData() {
          ??????????? DBConnectionPoolImpl?cpi = new DBConnectionPoolImpl() ;
          ??????????? Connection conn = cpi.getConnection() ;
          ??????????? // use conn to retrieve data from db
          ??????????? …
          ??????????? conn.close() ;
          }
          這樣更符合普通用戶的編碼習慣。但是可以這么編碼的前提是:
          1、 close 函數要將連接對象還給連接池,而不是關閉物理的數據庫連接。
          2、 所有 Connection 的其他函數必須能夠正常工作。
          也就是說需要特殊處理 close 函數,而對其他函數直接進行轉發就可以了。
          用最直接的方法實現如下:
          class ConnectionProxy implements Connection {
          ??????????? private Connection realConn ;
          ??????????? private DBConnectionPool dbcp ;
          ??????????? public ConnectionProxy( Connection conn?,?DBConnectionPool pool ) {
          ??????????????????????? realConn = conn ;
          ??????????????????????? dbcp = pool ;
          }
          ?
          public void close() throws SQLException {
          ????????? dbcp. releaseConnection( realConn ) ;
          }
          ?
          public PreparedStatement prepareStatement(String sql, String columnNames[]) throws SQLException {
          ?????????
          ????????? return realConn.prepareStatement( sql , columnNames ) ;
          }
          //??????? 所有的其他Connection接口中的方法轉發
          }
          可以看出這樣的實現代碼很多,而且幾乎都是相同的。當然可以編寫程序來寫出這樣的代碼,如果使用 DynamicProxy ,那么整個實現就比較優雅了。
          ?
          Classs ConnectionProxy InvocationHandler {
          ?? Connection conn ;
          ?? DBConnectionPool cp ;
          Public ConnectionProxy( Connection conn , DBConnectionPool cp ) {
          This.conn = conn ;
          This.cp = cp ;
          }
          public Object invoke(Object proxy, Method method, Object[] args)
          ????? throws Throwable {
          ???????????? Object result = null ;
          ???????????? if ( “close”.equals( method.getName()?) {
          ?????????????????? cp. releaseConnection( conn ) ;
          } else {
          ?????? ??? ?result = method.invoke( obj , args )?;
          }
          ?????????? Return result ;
          ????? }
          }
          有個類 ConnectionProxy后,我們只需要讓 DBConnectionPool 的方法 getConnection 返回動態代理即可。實現如下:
          class DBConnectionPoolImpl implements DBConnectionPool {
          ??????????? public Connection getConnection() {
          ??????????????????????? Connection conn?;
          ??????????????????????? // 從池中取得連接或建立連接
          ??????????????????????? return (Connection)Proxy.newInstance(
          ????? ????????????? Connection.class.getClassLoader(),
          ??? ????????????????????? new Class[]{ Connection.class },
          ???????????? ?????? new ConnectionProxy( conn ) ?) ;
          ?
          }
          }
          這樣就實現了連接池。
          jdk1.5 提供的用于 rmi dynamic stub 也使用 dynamic proxy 技術。只要你認真研究,其實很多問題都可以使用 dynamic proxy 來解決。

          posted on 2006-03-24 13:34 chenhui 閱讀(847) 評論(0)  編輯  收藏 所屬分類: 設計模式


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


          網站導航:
           

          導航

          統計

          常用鏈接

          留言簿(1)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          介紹 IOC

          友情鏈接

          最新隨筆

          搜索

          積分與排名

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 天津市| 凭祥市| 上杭县| 安化县| 华宁县| 扎鲁特旗| 筠连县| 嵊州市| 仪陇县| 乌拉特中旗| 普格县| 平南县| 特克斯县| 平谷区| 奉化市| 苍南县| 潞城市| 涿鹿县| 金秀| 竹北市| 小金县| 青州市| 许昌县| 印江| 雅安市| 建昌县| 百色市| 南平市| 延安市| 政和县| 乐业县| 奎屯市| 马鞍山市| 扶风县| 贡山| 盐城市| 哈尔滨市| 四会市| 昭觉县| 巴中市| 惠东县|