Xiaobo Sun

          Eclipse-Unix http://umlfact.berlios.de/~s_xsun/

          2008年12月14日

          Java annotation

          版權聲明:本文可以自由轉載,轉載時請務必以超鏈接形式標明文章原始出處和作者信息及本聲明
          作者:cleverpig(作者的Blog:http://blog.matrix.org.cn/page/cleverpig)
          原文:http://www.matrix.org.cn/resource/article/44/44048_Java+Annotation.html
          關鍵字:Java,annotation,標注


          摘要:
          本文針對java初學者或者annotation初次使用者全面地說明了annotation的使用方法、定義方式、分類。初學者可以通過以上的說明制作 簡單的annotation程序,但是對于一些高級的annotation應用(例如使用自定義annotation生成javabean映射xml文 件)還需要進一步的研究和探討。涉及到深入annotation的內容,作者將在后文《Java Annotation高級應用》中談到。

          同時,annotation運行存在兩種方式:運行時、編譯時。上文中討論的都是在運行時的annotation應用,但在編譯時的annotation應用還沒有涉及,

          一、為什么使用Annotation:

          在JAVA應用中,我們常遇到一些需要使用模版代碼。例如,為了編寫一個JAX-RPC web service,我們必須提供一對接口和實現作為模版代碼。如果使用annotation對遠程訪問的方法代碼進行修飾的話,這個模版就能夠使用工具自動生成。
          另外,一些API需要使用與程序代碼同時維護的附屬文件。例如,JavaBeans需要一個BeanInfo Class與一個Bean同時使用/維護,而EJB則同樣需要一個部署描述符。此時在程序中使用annotation來維護這些附屬文件的信息將十分便利 而且減少了錯誤。

          二、Annotation工作方式:

          在5.0版之前的Java平臺已經具有了一些ad hoc annotation機制。比如,使用transient修飾符來標識一個成員變量在序列化子系統中應被忽略。而@deprecated這個 javadoc tag也是一個ad hoc annotation用來說明一個方法已過時。從Java5.0版發布以來,5.0平臺提供了一個正式的annotation功能:允許開發者定義、使用 自己的annoatation類型。此功能由一個定義annotation類型的語法和一個描述annotation聲明的語法,讀取annotaion 的API,一個使用annotation修飾的class文件,一個annotation處理工具(apt)組成。
          annotation并不直接影響代碼語義,但是它能夠工作的方式被看作類似程序的工具或者類庫,它會反過來對正在運行的程序語義有所影響。annotation可以從源文件、class文件或者以在運行時反射的多種方式被讀取。
          當然annotation在某種程度上使javadoc tag更加完整。一般情況下,如果這個標記對java文檔產生影響或者用于生成java文檔的話,它應該作為一個javadoc tag;否則將作為一個annotation。

          三、Annotation使用方法:

          1。類型聲明方式:
          通常,應用程序并不是必須定義annotation類型,但是定義annotation類型并非難事。Annotation類型聲明于一般的接口聲明極為類似,區別只在于它在interface關鍵字前面使用“@”符號。
          annotation類型的每個方法聲明定義了一個annotation類型成員,但方法聲明不必有參數或者異常聲明;方法返回值的類型被限制在以下的范 圍:primitives、String、Class、enums、annotation和前面類型的數組;方法可以有默認值。

          下面是一個簡單的annotation類型聲明:
          清單1:


               /**

                * Describes the Request-For-Enhancement(RFE) that led

                * to the presence of the annotated API element.

                */

               public @interface RequestForEnhancement {

                   int     id();

                   String synopsis();

                   String engineer() default "[unassigned]";

                   String date();     default "[unimplemented]";

               }


          代碼中只定義了一個annotation類型RequestForEnhancement。

          2。修飾方法的annotation聲明方式:
          annotation是一種修飾符,能夠如其它修飾符(如public、static、final)一般使用。習慣用法是annotaions用在其它的 修飾符前面。annotations由“@+annotation類型+帶有括號的成員-值列表”組成。這些成員的值必須是編譯時常量(即在運行時不 變)。

          A:下面是一個使用了RequestForEnhancement annotation的方法聲明:
          清單2:


               @RequestForEnhancement(

                   id        = 2868724,

                   synopsis = "Enable time-travel",

                   engineer = "Mr. Peabody",

                   date      = "4/1/3007"

               )

               public static void travelThroughTime(Date destination) { ... }



          B:當聲明一個沒有成員的annotation類型聲明時,可使用以下方式:
          清單3:


               /**

                * Indicates that the specification of the annotated API element

                * is preliminary and subject to change.

                */

               public @interface Preliminary { }



          作為上面沒有成員的annotation類型聲明的簡寫方式:
          清單4:


               @Preliminary public class TimeTravel { ... }



          C:如果在annotations中只有唯一一個成員,則該成員應命名為value:
          清單5:


               /**

                * Associates a copyright notice with the annotated API element.

                */

               public @interface Copyright {

                   String value();

               }



          更為方便的是對于具有唯一成員且成員名為value的annotation(如上文),在其使用時可以忽略掉成員名和賦值號(=):
          清單6:


               @Copyright("2002 Yoyodyne Propulsion Systems")

               public class OscillationOverthruster { ... }



          3。一個使用實例:
          結合上面所講的,我們在這里建立一個簡單的基于annotation測試框架。首先我們需要一個annotation類型來表示某個方法是一個應該被測試工具運行的測試方法。
          清單7:


               import java.lang.annotation.*;



               /**

                * Indicates that the annotated method is a test method.

                * This annotation should be used only on parameterless static methods.

                */

               @Retention(RetentionPolicy.RUNTIME)

               @Target(ElementType.METHOD)

               public @interface Test { }



          值得注意的是annotaion類型聲明是可以標注自己的,這樣的annotation被稱為“meta-annotations”。

          在上面的代碼中,@Retention(RetentionPolicy.RUNTIME)這個meta-annotation表示了此類型的 annotation將被虛擬機保留使其能夠在運行時通過反射被讀取。而@Target(ElementType.METHOD)表示此類型的 annotation只能用于修飾方法聲明。

          下面是一個簡單的程序,其中部分方法被上面的annotation所標注:
          清單8:


               public class Foo {

                   @Test public static void m1() { }

                   public static void m2() { }

                   @Test public static void m3() {

                       throw new RuntimeException("Boom");

                   }

                   public static void m4() { }

                   @Test public static void m5() { }

                   public static void m6() { }

                   @Test public static void m7() {

                       throw new RuntimeException("Crash");

                   }

                   public static void m8() { }

               }



          Here is the testing tool:



               import java.lang.reflect.*;



               public class RunTests {

                  public static void main(String[] args) throws Exception {

                     int passed = 0, failed = 0;

                     for (Method m : Class.forName(args[0]).getMethods()) {

                        if (m.isAnnotationPresent(Test.class)) {

                           try {

                              m.invoke(null);

                              passed++;

                           } catch (Throwable ex) {

                              System.out.printf("Test %s failed: %s %n", m, ex.getCause());

                              failed++;

                           }

                        }

                     }

                     System.out.printf("Passed: %d, Failed %d%n", passed, failed);

                  }

               }



          這個程序從命令行參數中取出類名,并且遍歷此類的所有方法,嘗試調用其中被上面的測試annotation類型標注過的方法。在此過程中為了找出哪些方法 被annotation類型標注過,需要使用反射的方式執行此查詢。如果在調用方法時拋出異常,此方法被認為已經失敗,并打印一個失敗報告。最后,打印運 行通過/失敗的方法數量。
          下面文字表示了如何運行這個基于annotation的測試工具:

          清單9:


               $ java RunTests Foo

               Test public static void Foo.m3() failed: java.lang.RuntimeException: Boom

               Test public static void Foo.m7() failed: java.lang.RuntimeException: Crash

               Passed: 2, Failed 2



          四、Annotation分類:

          根據annotation的使用方法和用途主要分為以下幾類:

          1。內建Annotation——Java5.0版在java語法中經常用到的內建Annotation:
          @Deprecated用于修飾已經過時的方法;
          @Override用于修飾此方法覆蓋了父類的方法(而非重載);
          @SuppressWarnings用于通知java編譯器禁止特定的編譯警告。

          下面代碼展示了內建Annotation類型的用法:
          清單10:


          package com.bjinfotech.practice.annotation;



          /**

          * 演示如何使用java5內建的annotation

          * 參考資料:

          * http://java.sun.com/docs/books/tutorial/java/javaOO/annotations.html

          * http://java.sun.com/j2se/1.5.0/docs/guide/language/annotations.html

          * http://mindprod.com/jgloss/annotations.html

          * @author cleverpig

          *

          */

          import java.util.List;



          public class UsingBuiltInAnnotation {

                   //食物類

                   class Food{}

                   //干草類

                   class Hay extends Food{}

                   //動物類

                   class Animal{

                           Food getFood(){

                                   return null;

                           }

                           //使用Annotation聲明Deprecated方法

                           @Deprecated

                           void deprecatedMethod(){

                           }

                   }

                   //馬類-繼承動物類

                   class Horse extends Animal{

                           //使用Annotation聲明覆蓋方法

                           @Override

                           Hay getFood(){

                                   return new Hay();

                           }

                           //使用Annotation聲明禁止警告

                           @SuppressWarnings({"deprecation","unchecked"})

                           void callDeprecatedMethod(List horseGroup){

                                   Animal an=new Animal();

                                   an.deprecatedMethod();

                                   horseGroup.add(an);

                           }

                   }

          }



          2。開發者自定義Annotation:由開發者自定義Annotation類型。
          下面是一個使用annotation進行方法測試的sample:

          AnnotationDefineForTestFunction類型定義如下:
          清單11:


          package com.bjinfotech.practice.annotation;



          import java.lang.annotation.*;

          /**

          * 定義annotation

          * @author cleverpig

          *

          */

          //加載在VM中,在運行時進行映射

          @Retention(RetentionPolicy.RUNTIME)

          //限定此annotation只能標示方法

          @Target(ElementType.METHOD)

          public @interface AnnotationDefineForTestFunction{}



          測試annotation的代碼如下:

          清單12:


          package com.bjinfotech.practice.annotation;



          import java.lang.reflect.*;



          /**

          * 一個實例程序應用前面定義的Annotation:AnnotationDefineForTestFunction

          * @author cleverpig

          *

          */

          public class UsingAnnotation {

                   @AnnotationDefineForTestFunction public static void method01(){}

                  

                   public static void method02(){}

                  

                   @AnnotationDefineForTestFunction public static void method03(){

                           throw new RuntimeException("method03");

                   }

                  

                   public static void method04(){

                           throw new RuntimeException("method04");

                   }

                  

                   public static void main(String[] argv) throws Exception{

                           int passed = 0, failed = 0;

                           //被檢測的類名

                           String className="com.bjinfotech.practice.annotation.UsingAnnotation";

                           //逐個檢查此類的方法,當其方法使用annotation聲明時調用此方法

                       for (Method m : Class.forName(className).getMethods()) {

                          if (m.isAnnotationPresent(AnnotationDefineForTestFunction.class)) {

                             try {

                                m.invoke(null);

                                passed++;

                             } catch (Throwable ex) {

                                System.out.printf("測試 %s 失敗: %s %n", m, ex.getCause());

                                failed++;

                             }

                          }

                       }

                       System.out.printf("測試結果: 通過: %d, 失敗: %d%n", passed, failed);

                   }

          }



          3。使用第三方開發的Annotation類型
          這也是開發人員所常常用到的一種方式。比如我們在使用Hibernate3.0時就可以利用Annotation生成數據表映射配置文件,而不必使用Xdoclet。

          五、總結:

          1。前面的文字說明了annotation的使用方法、定義方式、分類。初學者可以通過以上的說明制作簡單的annotation程序,但是對于一些高級 的annotation應用(例如使用自定義annotation生成javabean映射xml文件)還需要進一步的研究和探討。

          2。同時,annotation運行存在兩種方式:運行時、編譯時。上文中討論的都是在運行時的annotation應用,但在編譯時的 annotation應用還沒有涉及,因為編譯時的annotation要使用annotation processing tool。

          涉及以上2方面的深入內容,作者將在后文《Java Annotation高級應用》中談到。

          posted @ 2009-05-14 13:04 Xiaobo Sun 閱讀(218) | 評論 (0)編輯 收藏

          google tips

          =========================================================
          GOOGLE不支持通配符,如“*”、“?”等,只能做精確查詢,關鍵字后面的“*”或者“?”會被忽略掉。

          GOOGLE對英文字符大小寫不敏感,“GOD”和“god”搜索的結果是一樣的。

          GOOGLE的關鍵字可以是詞組(中間沒有空格),也可以是句子(中間有空格),但是,用句子做關鍵字,必須加英文引號。

          示例:搜索包含“long, long ago”字串的頁面。
          搜索:“"long, long ago"”
          結果:已向英特網搜索"long, long ago". 共約有28,300項查詢結果,這是第1-10項。搜索用時0.28秒。

          注意:和搜索英文關鍵字串不同的是,GOOGLE對中文字串的處理并不十分完善。比如,搜索“"啊,我的太陽"”,我們希望結果中含有這個句子,事實并非 如此。查詢的很多結果,“啊”、“我的”、“太陽”等詞語是完全分開的,但又不是“啊 我的 太陽”這樣的與查詢。顯然,GOOGLE對中文的支持尚有欠缺之處。

          GOOGLE對一些網路上出現頻率極高的詞(主要是英文單詞),如“i”、“com”,以及一些符號如“*”、“.”等,作忽略處理,如果用戶必須要求關鍵字中包含這些常用詞,就要用強制語法“+”。

          示例:搜索包含“Who am I ?”的網頁。如果用“"who am i ?"”,“Who”、“I”、“?”會被省略掉,搜索將只用“am”作關鍵字,所以應該用強制搜索。
          搜索:“"+who +am +i"”
          結果:已向英特網搜索"+who +am +i". 共約有362,000項查詢結果,這是第1-10項。搜索用時0.30秒。

          注意:英文符號(如問號,句號,逗號等)無法成為搜索關鍵字,加強制也不行。

          ==============================================================
          inurl語法返回的網頁鏈接中包含第一個關鍵字,后面的關鍵字則出現在鏈接中或者網頁文檔中。有很多網站把某一類具有相同屬性的資源名稱顯示在目錄名稱 或者網頁名稱中,比如“MP3”、“GALLARY”等,于是,就可以用INURL語法找到這些相關資源鏈接,然后,用第二個關鍵詞確定是否有某項具體資 料。INURL語法和基本搜索語法的最大區別在于,前者通常能提供非常精確的專題資料。

          示例:查找MIDI曲“滄海一聲笑”。
          搜索:“inurl:midi 滄海一聲笑”
          結果:已搜索有關inurl:midi 滄海一聲笑的中文(簡體)網頁。共約有14項查詢結果,這是第1-10項。搜索用時0.01秒。

          示例:查找微軟網站上關于windows2000的安全課題資料。
          搜索:“inurl:security windows2000 site:microsoft.com”
          結果:已在microsoft.com內搜索有關 inurl:security windows2000的網頁。共約有198項查詢結果,這是第1-10項。搜索用時0.37秒。

          注意:“inurl:”后面不能有空格,GOOGLE也不對URL符號如“/”進行搜索。GOOGLE對“cgi-bin/phf”中的“/”當成空格處理。

          posted @ 2009-05-14 12:41 Xiaobo Sun 閱讀(216) | 評論 (0)編輯 收藏

          MySQL

          1。啟動:mysqld --console
          2。調試:mysql -u root

          posted @ 2009-05-06 17:16 Xiaobo Sun 閱讀(201) | 評論 (0)編輯 收藏

          void* (like Object in Java)

          void   *從本質上講是一種指針的類型,就像   (char   *)、(int   *)類型一樣.但是其又具有  
            特殊性,它可以存放其他任何類型的指針類型:例如:  
                                                  char   *array="I   am   the   pointer   of   string";  
                                                  void   *   temp;                   //temp可以存放其他任何類型的指針(地址)  
                                                  temp=array;                       //   temp   的指針類型  
                                                  cout<<array<<endl;  
                                                  cout<<temp<<endl;  
                                                  cout<<(char   *)temp<<endl;  
            運行結果:  
                                                I   am   the   pointer   of   string  
                                                0x0042510C   (這個值就是array指針變量所存儲的值)  
                                                I   am   the   pointer   of   string  
             
             
             
            2.但是不能將void*類型的值賦給其他既定的類型,除非經過顯示轉換:   例如:  
                                                                              int   a=20;  
                                                                              int   *   pr=&a;  
                                                                                void   *p;  
                                                                                pr=p               //error,不能將空的類型賦給int   *  
           
                                                                                pr=(int   *)p;     //ok,經過轉換 

          posted @ 2009-02-05 14:07 Xiaobo Sun 閱讀(279) | 評論 (0)編輯 收藏

          Servlet Lifecycle

          begin with first request : web.xml - init
          end when container is hsut down: web.xml - destroy

          By default setting: each Servlet has a Threadpool to support multithreads.

          posted @ 2009-01-09 23:41 Xiaobo Sun 閱讀(253) | 評論 (0)編輯 收藏

          Java Class Loader

           Class loader priority is bootstrap >extension >application (or system)

          1. bootstrap: 主要是負責裝載jre/lib下的jar文件,當然,你也可以通過-Xbootclasspath參數定義。該ClassLoader不能被Java代碼實例化,因為它是JVM本身的一部分
          2. extension: 該ClassLoader是Bootstrap classLoader的子class loader。它主要負責加載jre/lib/ext/下的所有jar文件。只要jar包放置這個位置,就會被虛擬機加載。一個常見的、類似的問題是,你 將mysql的低版本驅動不小心放置在這兒,但你的Web應用程序的lib下有一個新的jdbc驅動,但怎么都報錯,譬如不支持JDBC2.0的 DataSource,這時你就要當心你的新jdbc可能并沒有被加載。這就是ClassLoader的delegate現象。常見的有log4j、 common-log、dbcp會出現問題,因為它們很容易被人塞到這個ext目錄,或是Tomcat下的common/lib目錄。
          3. application loader: 也稱為System ClassLoaer。它負責加載CLASSPATH環境變量下的classes。缺省情況下,它是用戶創建的任何ClassLoader的父 ClassLoader,我們創建的standalone應用的main class缺省情況下也是由它加載(通過Thread.currentThread().getContextClassLoader()查看)。

          我們實際開發中,用ClassLoader更多時候是用其加載classpath下的資源,特別是配置文件,如ClassLoader.getResource(),比FileInputStream直接。

          ClassLoader是一種分級(hierarchy)的代理(delegation)模型。
          Delegation:其實是Parent Delegation,當需要加載一個class時,當前線程的ClassLoader首先會將請求代理到其父classLoader,遞歸向上,如果該 class已經被父classLoader加載,那么直接拿來用,譬如典型的ArrayList,它最終由Bootstrap ClassLoader加載。并且,每個ClassLoader只有一個父ClassLoader。
          Class查找的位置和順序依次是:Cache、parent、self。
          Hierarchy: 上面的delegation已經暗示了一種分級結構,同時它也說明:一個ClassLoader只能看到被它自己加載的 classes,或是看到其父(parent) ClassLoader或祖先(ancestor) ClassLoader加載的Classes。
          在一個單虛擬機環境下,標識一個類有兩個因素:class的全路徑名、該類的ClassLoader。

          ===================Tomcat Class Loading==========================================


          posted @ 2008-12-21 12:23 Xiaobo Sun 閱讀(239) | 評論 (0)編輯 收藏

          Design Pattern: delegation

           class A {
               void f() { System.out.println("A: doing f()"); }
               void g() { System.out.println("A: doing g()"); }
          }

          class C {
               // delegation
               A a = new A();

               void f() { a.f(); }
               void g() { a.g(); }

               // normal attributes
               X x = new X();
               void y() { /* do stuff */ }
          }

          public class Main {
               public static void main(String[] args) {
                   C c = new C();
                   c.f();
                   c.g();
               }
          }

          posted @ 2008-12-14 09:34 Xiaobo Sun 閱讀(289) | 評論 (0)編輯 收藏

          Design Pattern: Proxy, dynamic Proxy

          代理模式

           Proxy Pattern's 3 roles:

          1.  (abstract common)Subject:common interface

          2.  ProxySubject:含有the reference to the RealSubject //delegation

          3.  RealSubject:實現邏輯的類

          類圖如下:

          圖1

          Java 動態代理

          從JDK1.3開始,Java就引入了動態代理的概念。動態代理(Dynamic Proxy)可以幫助你減少代碼行數,真正提高代碼的可復用度。

           類圖如下:

          圖2 

          動態代理和普通的代理模式的區別,就是動態代理中的代理類是由java.lang.reflect.Proxy類在運行期時根據接口定義,采用Java反射功能動態生成的(圖2的匿名實現類)。和java.lang.reflect.InvocationHandler結合,可以加強現有類的方法實現。如圖2,圖中的自定義Handler實現InvocationHandler接口,自定義Handler實例化時,將實現類傳入自定義Handler對象。自定義Handler需要實現invoke方法,該方法可以使用Java反射調用實現類的實現的方法,同時當然可以實現其他功能,例如在調用實現類方法前后加入Log。而Proxy類根據Handler和需要代理的接口動態生成一個接口實現類的對象。當用戶調用這個動態生成的實現類時,實際上是調用了自定義Handler的invoke方法。

          下面是使用動態代理的步驟:

          1.  Client向Proxy請求一個具有某個功能的實例;

          2.  Proxy根據Subject,以自定義Handler創建一個匿名內部類,并返回給Client;

          3.  Client獲取該匿名內部類的引用,調用在Subject接口種定義的方法;

          4.  匿名內部類將對方法的調用轉換為對自定義Handler中invoke方法的調用

          5. invoke方法根據一些規則做處理,如記錄log,然后調用SubjectImpl中的方法

           

          Examples

           

          Here is a simple example that prints out a message before and after a method invocation on an object that implements an arbitrary list of interfaces:

          public interface Foo {
          Object bar(Object obj) throws BazException;
          }
          public class FooImpl implements Foo {
          Object bar(Object obj) throws BazException {
          // ...
          }
          }
          public class DebugProxy implements java.lang.reflect.InvocationHandler {
          private Object obj;
          public static Object newInstance(Object obj) {
          return java.lang.reflect.Proxy.newProxyInstance(
          obj.getClass().getClassLoader(),
          obj.getClass().getInterfaces(),
          new DebugProxy(obj));
          }
          private DebugProxy(Object obj) {
          this.obj = obj;
          }
          public Object invoke(Object proxy, Method m, Object[] args)
          throws Throwable
          {
          Object result;
          try {
          System.out.println("before method " + m.getName());
          result = m.invoke(obj, args);
          } catch (InvocationTargetException e) {
          throw e.getTargetException();
          } catch (Exception e) {
          throw new RuntimeException("unexpected invocation exception: " +
          e.getMessage());
          } finally {
          System.out.println("after method " + m.getName());
          }
          return result;
          }
          }
          

          To construct a DebugProxy for an implementation of the Foo interface and call one of its methods:

              Foo foo = (Foo) DebugProxy.newInstance(new FooImpl());
          foo.bar(null);
          


           

          posted @ 2008-12-14 09:21 Xiaobo Sun 閱讀(397) | 評論 (0)編輯 收藏

          <2008年12月>
          30123456
          78910111213
          14151617181920
          21222324252627
          28293031123
          45678910

          導航

          統計

          常用鏈接

          留言簿(3)

          隨筆分類

          隨筆檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 孝昌县| 白沙| 朝阳市| 治县。| 专栏| 梅州市| 历史| 大城县| 洪江市| 黔江区| 永登县| 保定市| 定兴县| 平山县| 南安市| 延津县| 黎城县| 集贤县| 陇西县| 阜康市| 安岳县| 柳江县| 定远县| 景谷| 海南省| 江门市| 武宁县| 玛沁县| 西林县| 北宁市| 隆安县| 兴义市| 闽侯县| 苗栗市| 紫金县| 牟定县| 九台市| 阳东县| 宁南县| 比如县| 水富县|