seaairland

           

          Dynamic Proxy介紹

          一、 proxy模式簡介
          GoF介紹了proxy模式。代理對象為其他對象提供一種代理以控制對這個對象的訪問。它靜態(tài)結(jié)構(gòu)如下:
          Client 需要訪問 RealSubject 時,它實際訪問的是 Proxy 對象,而后 Proxy 對象將請求委托給 RealSubject RealSubject 實現(xiàn)了主要的邏輯, Proxy 對象可以在處理請求之前、之后作額外的處理。可以看出, Proxy RealSubject 實現(xiàn)了同樣的接口,這樣 Client 才可以調(diào)用 RealSubject 實現(xiàn)的所有 Subject 的方法。
          我們在實現(xiàn) Proxy 時,如果使用的是 C++ 語言,我們可以重載操作符 -> 來實現(xiàn)代理。優(yōu)點是實現(xiàn)簡單,缺點是它不能區(qū)別對待不同的請求。當(dāng)然也可以是用普通的形式,創(chuàng)建一個代理類,實現(xiàn)接口,并將調(diào)用委托給被代理的對象。
          如果使用的是 Java 語言,我們當(dāng)然可以使用普通的形式來實現(xiàn) Proxy 模式。但是 JDK1.3 引入了 dynamic proxy ,它允許我們更容易的實現(xiàn)代理。
          二、 JDK中的Dynamic Proxy介紹
          它由 java.lang.reflect.Proxy java.lang.reflect.InvocationHandler 等組成。 Proxy 類擁有一個 protected InvocationHandler 類型的成員變量。 它只能代理 Interface。
          它的基本思想如下:
          1.???? 代理類由 Proxy 的靜態(tài)方法 getProxyClass 來動態(tài)的創(chuàng)建出來。該方法所需要的一個參數(shù)為它所代理的接口數(shù)組。創(chuàng)建出來的代理類實現(xiàn)了所有的接口,并且繼承了 Proxy
          2.???? 代理類實現(xiàn)的接口方法的處理邏輯為,調(diào)用父類的 InvocationHandler 類型的成員變量的 invoke 方法。
          由此可以看出,必須讓 Proxy 對象擁有一個正確的 InvocationHandler 的實現(xiàn)。 Proxy 對象由 Proxy 的靜態(tài)成員函數(shù) newProxyInstance 來創(chuàng)建,該函數(shù)的最后一個參數(shù)為 InvocationHandler 類型。動態(tài)生成的代理類實現(xiàn)的所有接口方法都被委托給 InvocationHandler 接口的 invoke 方法。
          三、 例子代碼
          假如有如下接口:
          interface Foo {
          ?void f(String s);
          ?void g(int i);
          ?String h(int i, String s);
          }
          并且有一個實現(xiàn)
          class Implement implements Foo {
          ?? …
          }
          現(xiàn)在我們想在 Foo 接口的每個方法調(diào)用時,加入日至。一個很簡單很直觀的方法如下:
          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”);
          可以看出這樣的實現(xiàn)代碼很多,而且?guī)缀醵际窍嗤摹.?dāng)然可以編寫程序來寫出這樣的代碼,但是我們?nèi)绻褂?/span> JDK1.3 引入的 dynamic proxy ,那么情況就完全不同了。
          四、 dynamic proxy實現(xiàn)Log的代碼
          1.?????? 編寫 InvocationHandler 的子類,來攔截所有的方法調(diào)用。
          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的靜態(tài)方法來創(chuàng)建代理類。?
          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.?????? 客戶在代理類上調(diào)用方法
          proxy. h( 10 , “str”);
          可以看出,如果接口中有很多方法,那么使用 dynamic proxy 是很合適的,但是如果接口只有很少的方法,可能使用普通的方法更直觀,也更簡單。
          五、 應(yīng)用例子
          如果我們設(shè)計一個數(shù)據(jù)庫連接池,接口如下:
          interface DBConnectionPool {
          ??????????? public java.sql.Connection getConnection() ;
          ??????????? public void releaseConnection( java.sql.Connection conn ) ;
          }
          class DBConnectionPoolImpl implements DBConnectionPool {
          ??????????? …
          }
          ?
          那么一個可能的用戶調(diào)用序列如下:
          void getData() {
          ??????????? DBConnectionPoolImpl?cpi = new DBConnectionPoolImpl() ;
          ??????????? Connection conn = cpi.getConnection() ;
          ??????????? // use conn to retrieve data from db
          ??????????? …
          ??????????? cpi. releaseConnection( conn ) ;
          ???????????
          }
          藍(lán)色的代碼表示了將連接還給連接池,因為所有的連接都是由連接池來管理的。但是這樣的代碼對用戶來講可能不太習(xí)慣,而且迫使用戶這樣編寫代碼,用戶會意識到 cpi 對連接作了特殊的處理。
          ??????????? 一個更好的方法是調(diào)用 Connection 接口的 close 方法。這樣的代碼如下:
          void getData() {
          ??????????? DBConnectionPoolImpl?cpi = new DBConnectionPoolImpl() ;
          ??????????? Connection conn = cpi.getConnection() ;
          ??????????? // use conn to retrieve data from db
          ??????????? …
          ??????????? conn.close() ;
          }
          這樣更符合普通用戶的編碼習(xí)慣。但是可以這么編碼的前提是:
          1、 close 函數(shù)要將連接對象還給連接池,而不是關(guān)閉物理的數(shù)據(jù)庫連接。
          2、 所有 Connection 的其他函數(shù)必須能夠正常工作。
          也就是說需要特殊處理 close 函數(shù),而對其他函數(shù)直接進(jìn)行轉(zhuǎn)發(fā)就可以了。
          用最直接的方法實現(xiàn)如下:
          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接口中的方法轉(zhuǎn)發(fā)
          }
          可以看出這樣的實現(xiàn)代碼很多,而且?guī)缀醵际窍嗤摹.?dāng)然可以編寫程序來寫出這樣的代碼,如果使用 DynamicProxy ,那么整個實現(xiàn)就比較優(yōu)雅了。
          ?
          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 返回動態(tài)代理即可。實現(xiàn)如下:
          class DBConnectionPoolImpl implements DBConnectionPool {
          ??????????? public Connection getConnection() {
          ??????????????????????? Connection conn?;
          ??????????????????????? // 從池中取得連接或建立連接
          ??????????????????????? return (Connection)Proxy.newInstance(
          ????? ????????????? Connection.class.getClassLoader(),
          ??? ????????????????????? new Class[]{ Connection.class },
          ???????????? ?????? new ConnectionProxy( conn ) ?) ;
          ?
          }
          }
          這樣就實現(xiàn)了連接池。
          jdk1.5 提供的用于 rmi dynamic stub 也使用 dynamic proxy 技術(shù)。只要你認(rèn)真研究,其實很多問題都可以使用 dynamic proxy 來解決。

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


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


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

          導(dǎo)航

          統(tǒng)計

          常用鏈接

          留言簿(1)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          介紹 IOC

          友情鏈接

          最新隨筆

          搜索

          積分與排名

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 合江县| 青冈县| 兴海县| 凤庆县| 平顺县| 郯城县| 禹州市| 任丘市| 高雄市| 台江县| 梅河口市| 宝山区| 东方市| 青河县| 交城县| 台江县| 凤凰县| 定安县| 金沙县| 保康县| 江陵县| 信宜市| 连江县| 青岛市| 依兰县| 江达县| 阳新县| 响水县| 陇川县| 柳河县| 孝义市| 柳江县| 白河县| 上虞市| 达州市| 顺义区| 湖州市| 莱芜市| 南靖县| 马鞍山市| 新龙县|