posts - 0, comments - 77, trackbacks - 0, articles - 356
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          反射和代理

          Posted on 2007-09-07 15:36 semovy 閱讀(308) 評論(0)  編輯  收藏 所屬分類: JAVA基礎

          一. 關于數據庫.
          當今的數據處理大致可以分成兩大類:聯機事務處理OLTP(on-line transaction processing)、聯機分析處理OLAP(On-Line Analytical Processing)。OLTP是傳統的關系型數據庫的主要應用,主要是基本的、日常的事務處理,例如銀行交易。OLAP是數據倉庫系統的主要應用,支持復雜的分析操作,側重決策支持,并且提供直觀易懂的查詢結果。下表列出了OLTP與OLAP之間的比較。

                                                 OLTP                                                              OLAP
          用戶                               操作人員,低層管理人員                               決策人員,高級管理人員
          功能                              日常操作處理                                                 分析決策
          DB 設計                        面向應用                                                        面向主題
          數據                               當前的, 最新的細節的, 二維的分立的         歷史的, 聚集的, 多維的集成的, 統一的
          存取                              讀/寫數十條記錄                                           讀上百萬條記錄
          工作單位                       簡單的事務                                                     復雜的查詢
          用戶數                         上千個                                                             上百個
          DB 大小                       100MB-GB                                                      100GB-TB

          二. Java中的類反射:
          反射就是把Java類中的各種成分映射成相應的java類.
          Reflection 是 Java 程序開發語言的特征之一,它允許運行中的 Java 程序對自身進行檢查,或者說“自審”,并能直接操作程序的內部屬性。

          1.檢測類:

          1.1 reflection的工作機制

          考慮下面這個簡單的例子,讓我們看看 reflection 是如何工作的。

          import java.lang.reflect.*;
          public class DumpMethods {
          public static void main(String args[]) {
             try {
              Class c = Class.forName(args[0]);
              Method m[] = c.getDeclaredMethods();
              for (int i = 0; i < m.length; i++)
              System.out.println(m[i].toString());
             }
             catch (Throwable e) {
              System.err.println(e);
             }
          }
          }

          按如下語句執行:

          java DumpMethods java.util.Stack

          它的結果輸出為:

          public java.lang.Object java.util.Stack.push(java.lang.Object)

          public synchronized java.lang.Object java.util.Stack.pop()

          public synchronized java.lang.Object java.util.Stack.peek()

          public boolean java.util.Stack.empty()

          public synchronized int java.util.Stack.search(java.lang.Object)

          這樣就列出了java.util.Stack 類的各方法名以及它們的限制符和返回類型。

          這個程序使用 Class.forName 載入指定的類,然后調用 getDeclaredMethods 來獲取這個類中定義了的方法列表。java.lang.reflect.Methods 是用來描述某個類中單個方法的一個類。

          1.2 Java類反射中的主要方法

          對于以下三類組件中的任何一類來說 -- 構造函數、字段和方法 -- java.lang.Class 提供四種獨立的反射調用,以不同的方式來獲得信息。調用都遵循一種標準格式。以下是用于查找構造函數的一組反射調用:

          Constructor getConstructor(Class[] params) -- 獲得使用特殊的參數類型的公共構造函數,

          Constructor[] getConstructors() -- 獲得類的所有公共構造函數

          Constructor getDeclaredConstructor(Class[] params) -- 獲得使用特定參數類型的構造函數(與接入級別無關)

          Constructor[] getDeclaredConstructors() -- 獲得類的所有構造函數(與接入級別無關)

          獲得字段信息的Class 反射調用不同于那些用于接入構造函數的調用,在參數類型數組中使用了字段名:

          Field getField(String name) -- 獲得命名的公共字段

          Field[] getFields() -- 獲得類的所有公共字段

          Field getDeclaredField(String name) -- 獲得類聲明的命名的字段

          Field[] getDeclaredFields() -- 獲得類聲明的所有字段

          用于獲得方法信息函數:

          Method getMethod(String name, Class[] params) -- 使用特定的參數類型,獲得命名的公共方法

          Method[] getMethods() -- 獲得類的所有公共方法

          Method getDeclaredMethod(String name, Class[] params) -- 使用特寫的參數類型,獲得類聲明的命名的方法

          Method[] getDeclaredMethods() -- 獲得類聲明的所有方法


          1.3開始使用 Reflection:

          用于 reflection 的類,如 Method,可以在 java.lang.relfect 包中找到。使用這些類的時候必須要遵循三個步驟:第一步是獲得你想操作的類的 java.lang.Class 對象。在運行中的 Java 程序中,用 java.lang.Class 類來描述類和接口等。

          下面就是獲得一個 Class 對象的方法之一:

          Class c = Class.forName("java.lang.String");

          這條語句得到一個 String 類的類對象。還有另一種方法,如下面的語句:

          Class c = int.class;

          或者

          Class c = Integer.TYPE;

          它們可獲得基本類型的類信息。其中后一種方法中訪問的是基本類型的封裝類 (如 Integer) 中預先定義好的 TYPE 字段。

          第二步是調用諸如 getDeclaredMethods 的方法,以取得該類中定義的所有方法的列表。

          一旦取得這個信息,就可以進行第三步了——使用 reflection API 來操作這些信息,如下面這段代碼:

          Class c = Class.forName("java.lang.String");

          Method m[] = c.getDeclaredMethods();

          System.out.println(m[0].toString());

          它將以文本方式打印出 String 中定義的第一個方法的原型。

          2.處理對象:

          如果要作一個開發工具像debugger之類的,你必須能發現filed values,以下是三個步驟:

          a.創建一個Class對象
          b.通過getField 創建一個Field對象
          c.調用Field.getXXX(Object)方法(XXX是Int,Float等,如果是對象就省略;Object是指實例).

          例如:
          import java.lang.reflect.*;
          import java.awt.*;

          class SampleGet {  
          public static void main(String[] args) {
             Rectangle r = new Rectangle(100, 325);
             printHeight(r);  
          }

          static void printHeight(Rectangle r) {
             Field heightField;
             Integer heightValue;
             Class c = r.getClass();
             try {
              heightField = c.getField("height");
              heightValue = (Integer) heightField.get(r);
              System.out.println("Height: " + heightValue.toString());
             } catch (NoSuchFieldException e) {
              System.out.println(e);
             } catch (SecurityException e) {
              System.out.println(e);
             } catch (IllegalAccessException e) {
              System.out.println(e);
             }
          }
          }

          顧名思義,反射 (reflection) 機制就像是在吳承恩所寫的西游記中所提及的「照妖鏡」,可以讓類別或對象 (object) 在執行時期「現出原形」。我們可以利用反射機制來深入了解某類(class) 的構造函數 (constructor)、方法 (method)、字段 (field),甚至可以改變字段的值、呼叫方法、建立新的對象。有了反射機制,程序員即使對所想使用的類別所知不多,也能照樣寫程序。反射機制能夠用來呼叫方法,這正是反射機制能夠取代函數指針的原因。

          以 Java 來說,java.lang.reflect.Method (以下簡稱 Method) 類別是用來表示某類別的某方法。我們可以透過 java.lang.Class (以下簡稱 Class) 類別的許多方法來取得 Method 對象。Method 類別提供 invoke() 方法,透過 invoke(),此 Method 對象所表示的方法可以被呼叫,所有的參數則是被組織成一個數組,以方便傳入 invoke()。

          舉個例子,下面是一個名為 Invoke 的程序,它會將命令列的 Java 類別名稱和要呼叫的方法名稱作為參數。為了簡單起見,我假定此方法是靜態的,且沒有參數:

          import java.lang.reflect.*;
          class Invoke {        
          public static void main(String[] args ) {               
             try {                       
              Class c = Class.forName( args[0] );                       
              Method m = c.getMethod( args[1], new Class [] { } );                       
              Object ret = m.invoke( null, null );                       
              System.out.println(args[0] + "." + args[1] +"() = " + ret );               
             } catch ( ClassNotFoundException ex ) {                       
              System.out.println("找不到此類別");               
             } catch (NoSuchMethodException ex ) {                       
              System.out.println("此方法不存在");               
             } catch (IllegalAccessException ex ) {                       
              System.out.println("沒有權限調用此方法");               
             } catch (InvocationTargetException ex ) {                       
              System.out.println("調用此方法時發生下列例外:\n" + ex.getTargetException() );               
             }        
          }
          }
          我們可以執行 Invoke 來取得系統的時間:

          java Invoke java.lang.System CurrentTimeMillis執行的結果如下所示:

          java.lang.System.currentTimeMillis() = 1049551169474我們的第一步就是用名稱去尋找指定的 Class。我們用類別名稱 (命令列的第一個參數) 去呼叫 forName() 方法,然后用方法名稱 (命令列的第二個參數) 去取得方法。getMethod() 方法有兩個參數:第一個是方法名稱 (命令列的第二個參數),第二個是 Class 對象的數組,這個陣例指明了方法的 signature (任何方法都可能會被多載,所以必須指定 signature 來分辨。) 因為我們的簡單程序只呼叫沒有參數的方法,我們建立一個 Class 對象的匿名空數組。如果我們想要呼叫有參數的方法,我們可以傳遞一個類別數組,數組的內容是各個類別的型態,依順序排列。

          一旦我們有了 Method 對象,就呼叫它的 invoke() 方法,這會造成我們的目標方法被調用,并且將結果以 Object 對象傳回。如果要對此對象做其它額外的事,你必須將它轉型為更精確的型態。

          invoke() 方法的第一個參數就是我們想要呼叫目標方法的對象,如果該方法是靜態的,就沒有對象,所以我們把第一個參數設為 null,這就是我們范例中的情形。第二個參數是要傳給目標方法作為參數的對象數組,它們的型態要符合呼叫 getMethod() 方法中所指定的型態。因為我們呼叫的方法沒有參數,所以我們傳遞 null 作為 invoke() 的第二個參數。


          import   java.lang.reflect.Constructor;
          import   java.lang.reflect.Field;
          import   java.lang.reflect.Method;
          import   java.lang.reflect.Modifier;
          import   java.util.HashMap;

          public    class   TestRef    {
                public    static    void   main(String[] args)   throws   Exception   {
                   TestRef testRef   =    new   TestRef();
                   Class clazz   =   TestRef. class ;
                   System.out.println( " getPackage() =   "    +   clazz.getPackage().getName());
                   //   getModifiers()的返回值可以包含類的種類信息。比如是否為public,abstract,static
                   int   mod   =   clazz.getModifiers();
                   System.out.println( " Modifier.isAbstract(mod) =   " + Modifier.isAbstract(mod));
                   System.out.println( " getName() =   " + clazz.getName());
                   System.out.println( " getSuperclass() =   " + clazz.getSuperclass().getName());
                   System.out.println( " getInterfaces() =   " + clazz.getInterfaces()); // 實現了哪些Interface
                   System.out.println( " clazz.getDeclaredClasses() =   " + clazz.getDeclaredClasses()); // 包含哪些內部類
                   System.out.println( " getDeclaringClass() =   " + clazz.getDeclaringClass());
                   // 如果clazz是inner class 那么返回其outer class
                   
                   System.out.println( " ---------- " );
                   Constructor[] constructor   =   clazz.getDeclaredConstructors(); // 返回一組構造函數 Constructor[]
                      if   (constructor   !=    null )   {
                        for   ( int   i   =    0 ; i   <   constructor.length; i ++ )    {
                           System.out.println(constructor[i].getName());
                       }
                   }
                  
                   System.out.println( " ---------- " );
                   Method[] method   =   clazz.getDeclaredMethods();   //   Method[]
                      if   (method   !=    null )   {
                        for   ( int   i   =    0 ; i   <   method.length; i ++ )    {
                           System.out.println(method[i].getName());
                       }
                   }
                  
                   System.out.println( " ---------- " );
                   Field[] field   =   clazz.getDeclaredFields();   //   Field[]
                      if   (field   !=    null )   {
                        for   ( int   i   =    0 ; i   <   field.length; i ++ )    {
                           System.out.println(field[i].getName());
                           System.out.println(field[i].getType().getName());
                           System.out.println(field[i].get(testRef));
                       }
                   }
                  
                   //   動態生成instance(無參數)
                   Class clz   =   Class.forName( " reflection.TestRef " );
                   Object obj   =   clz.newInstance();
                   System.out.println(((TestRef)obj).getStr());
                  
                   //   動態生成instance(有參數)
                    Class[] params   =    new   Class[]   {String. class ,   int . class ,   double . class } ;
                   Constructor construct   =   clz.getConstructor(params);
                   //   JDK1.5的情況下可以直接用{"haha",999,100.01}作為參數         
                    Object obj2   =   construct.newInstance(new Object[]{"haha",new Integer( 999 ),   new   Double( 100.01 )} );
                   System.out.println(((TestRef)obj2).getStr());
                  
                   //   動態調用method(public method)
                    Class[] params2   =    new   Class[]   {String. class } ;
                   Method methods   =   clz.getMethod( " setStr " , params2);
                   methods.invoke(testRef,   new   Object[]   { " invoke method " } );
                   System.out.println(testRef.getStr());
                  
                   //   動態改變field內容(public field)
                   Field fields   =   clz.getField( " str " );
                   fields.set(testRef,   " set field's value " );
                   System.out.println(testRef.getStr());
                  
               }

                public   TestRef()   {
                   System.out.println( " --- complete TestRef() --- " );
               }
              
               public   TestRef(String str,   int   i,   double   d)   {
                    this .str   =   str;
                    this .i   =   i;
                    this .d   =   d;
                   System.out.println( " --- complete TestRef(String str, int i, double d) --- " );
               }
              
               public   String str   =    " I'm a string " ;

               int   i   =    1 ;

               double   d   =    3.14 ;

               HashMap map   =    new   HashMap();

                public    double   getD()    {
                    return   d;
               }

                 public    void   setD( double   d)    {
                    this .d   =   d;
               }

                 public    int   getI()    {
                    return   i;
               }

                 public    void   setI( int   i)    {
                    this .i   =   i;
               }

                 public   HashMap getMap()    {
                    return   map;
               }

                 public    void   setMap(HashMap map)    {
                    this .map   =   map;
               }

                 public   String getStr()    {
                    return   str;
               }

                 public    void   setStr(String str)    {
                    this .str   =   str;
               }
          }

          三. 代理(Proxy)
          1.
          我們直接從代碼入手吧,我們可以使用一個動態代理類(Proxy),通過攔截一個對象的行為并添加我們需要的功能來完成。Java中的java.lang.reflect.Proxy類和java.lang.reflect.InvocationHandler接口為我們實現動態代理類提供了一個方案,但是該方案針對的對象要實現某些接口;如果針對的目的是類的話,cglib為我們提供了另外一個實現方案。等下會說明兩者的區別。
          一、接口的實現方案:
          1)首先編寫我們的業務接口(StudentInfoService.java):
          public interface StudentInfoService{
          void findInfo(String studentName);
          }
                及其實現類(StudentInfoServiceImpl.java):
          public class StudentInfoServiceImpl implements StudentInfoService{
          public void findInfo(String name){
             System.out.println("你目前輸入的名字是:"+name);
          }
          }
          2)現在我們需要一個日志功能,在findInfo行為之前執行并記錄其行為,那么我們就首先要攔截該行為。在實際執行的過程中用一個代理類來替我們完成。Java中為我們提供了實現動態代理類的方案:

          1'處理攔截目的的類(MyHandler.java)
          import org.apache.log4j.Logger;
          import java.lang.reflect.InvocationHandler;
          import java.lang.reflect.Proxy;
          import java.lang.reflect.Method;

          public class MyHandler implements InvocationHandler{
          private Object proxyObj;
          private static Logger log=Logger.getLogger(MyHandler.class);

          public Object bind(Object obj){
             this.proxyObj=obj;
             return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);
          }

          public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
             Object result=null;
             try{
              //請在這里插入代碼,在方法前調用
              log.info("調用log日志方法"+method.getName());
              result=method.invoke(proxyObj,args); //原方法
              //請在這里插入代碼,方法后調用
             }catch(Exception e){
              e.printStackTrace();
             }
             return result;
          }
          }
          2'我們實現一個工廠,為了方便我們使用該攔截類(AOPFactory.java):
          public class AOPFactory{
          private static Object getClassInstance(String clzName){
             Object obj=null;
             try{
              Class cls=Class.forName(clzName);
              obj=(Object)cls.newInstance();
             }catch(ClassNotFoundException cnfe){
              System.out.println("ClassNotFoundException:"+cnfe.getMessage());
             }catch(Exception e){
              e.printStackTrace();
             }
             return obj;
          }

          public static Object getAOPProxyedObject(String clzName){
             Object proxy=null;
             MyHandler handler=new MyHandler();
             Object obj=getClassInstance(clzName);
             if(obj!=null) {
              proxy=handler.bind(obj);
             }else{
              System.out.println("Can't get the proxyobj");
              //throw
             }
             return proxy;
          }
          }

          3)基本的攔截與其工廠我們都實現了,現在測試(ClientTest.java):
          public class ClientTest{
          public static void main(String[] args){
             StudentInfoService studentInfo=(StudentInfoService)AOPFactory.getAOPProxyedObject("StudentInfoServiceImpl");
             studentInfo.findInfo("阿飛");
          }
          }
          輸出結果(看你的log4j設置):
          [INFO]調用log日志方法findInfo
          你目前輸入的名字是:阿飛
                這樣我們需要的效果就出來了,業務處理自己在進行,但是我們實現了日志功能

          2.再看一個例子:
            假設系統由一系列的BusinessObject所完成業務邏輯功能,系統要求在每一次業務邏輯處理時要做日志記錄。這里我們略去具體的業務邏輯代碼。

          public interface BusinessInterface {
           public void processBusiness();
          }

          public class BusinessObject implements BusinessInterface {
           private Logger logger = Logger.getLogger(this.getClass().getName());
           public void processBusiness(){
            try {
             logger.info("start to processing...");
             //business logic here.
             System.out.println(“here is business logic”);
             logger.info("end processing...");
            } catch (Exception e){
             logger.info("exception happends...");
             //exception handling
            }
           }
          }

            這里處理商業邏輯的代碼和日志記錄代碼混合在一起,這給日后的維護帶來一定的困難,并且也會造成大量的代碼重復。完全相同的log代碼將出現在系統的每一個BusinessObject中。

          按照AOP的思想,我們應該把日志記錄代碼分離出來。要將這些代碼分離就涉及到一個問題,我們必須知道商業邏輯代碼何時被調用,這樣我們好插入日志記錄代碼。一般來說要截獲一個方法,我們可以采用回調方法或者動態代理。動態代理一般要更加靈活一些,目前多數的AOP Framework也大都采用了動態代理來實現。這里我們也采用動態代理作為例子。
          JDK1.2以后提供了動態代理的支持,程序員通過實現java.lang.reflect.InvocationHandler接口提供一個執行處理器,然后通過java.lang.reflect.Proxy得到一個代理對象,通過這個代理對象來執行商業方法,在商業方法被調用的同時,執行處理器會被自動調用。

            有了JDK的這種支持,我們所要做的僅僅是提供一個日志處理器。

          public class LogHandler implements InvocationHandler {

           private Logger logger = Logger.getLogger(this.getClass().getName());
            private Object delegate;
            public LogHandler(Object delegate){
             this.delegate = delegate;
            }

           public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object o = null;
            try {
             logger.info("method stats..." + method);
             o = method.invoke(delegate,args);
             logger.info("method ends..." + method);
            } catch (Exception e){
             logger.info("Exception happends...");
             //excetpion handling.
            }
            return o;
           }
          }

            現在我們可以把BusinessObject里面的所有日志處理代碼全部去掉了。

          public class BusinessObject implements BusinessInterface {

           private Logger logger = Logger.getLogger(this.getClass().getName());
           public void processBusiness(){
            //business processing
            System.out.println(“here is business logic”);
           }
          }

            客戶端調用商業方法的代碼如下:

          BusinessInterface businessImp = new BusinessObject();

          InvocationHandler handler = new LogHandler(businessImp);

          BusinessInterface proxy = (BusinessInterface) Proxy.newProxyInstance(
           businessImp.getClass().getClassLoader(),
           businessImp.getClass().getInterfaces(),
           handler);

          proxy.processBusiness();

            程序輸出如下:

          INFO: method stats...
          here is business logic
          INFO: method ends...

            至此我們的第一次小嘗試算是完成了。可以看到,采用AOP之后,日志記錄和業務邏輯代碼完全分開了,以后要改變日志記錄的話只需要修改日志記錄處理器就行了,而業務對象本身(BusinessObject)無需做任何修改。并且這個日志記錄不會造成重復代碼了,所有的商業處理對象都可以重用這個日志處理器。

          主站蜘蛛池模板: 山阳县| 伊金霍洛旗| 海南省| 锦屏县| 余江县| 南木林县| 灵台县| 游戏| 论坛| 永善县| 长寿区| 德州市| 恩平市| 平度市| 寿阳县| 达孜县| 珲春市| 开原市| 陇南市| 襄樊市| 尼木县| 鸡东县| 朝阳区| 江山市| 景宁| 霍林郭勒市| 彰化县| 九龙坡区| 尼勒克县| 汶上县| 玉屏| 绥化市| 海兴县| 万全县| 杭锦旗| 黎城县| 涞源县| 玉龙| 北碚区| 商河县| 常山县|