Java Annotation 必須掌握的特性

黃博文發表於2016-05-05

什麼是Annotation?

Annotation翻譯為中文即為註解,意思就是提供除了程式本身邏輯外的額外的資料資訊。Annotation對於標註的程式碼沒有直接的影響,它不可以直接與標註的程式碼產生互動,但其他元件可以使用這些資訊。

Annotation資訊可以被編譯進class檔案,也可以保留在Java 虛擬機器中,從而在執行時可以獲取。甚至對於Annotation本身也可以加Annotation。

那些物件可以加Annotation

類,方法,變數,引數,包都可以加Annotation。

內建的Annotation

@Override 過載父類中方法 @Deprecated 被標註的方法或型別已不再推薦使用

@SuppressWarnings 阻止編譯時的警告資訊。其需要接收一個String的陣列作為引數。 可供使用的引數有:

  • unchecked
  • path
  • serial
  • finally
  • fallthrough

可以用與其他annotation上的annotation

@Retention

確定Annotation被儲存的生命週期, 需要接收一個Enum物件RetentionPolicy作為引數。

public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}

@Documented 文件化

@Target

表示該Annotation可以修飾的範圍,接收一個Enum物件EnumType的陣列作為引數。

public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,

    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE
}

@Inherited

該Annotation可以影響到被標註的類的子類。

自定義Annotation

JSE5.0以後我們可以自定義Annotation。下面就是一個簡單的例子。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MethodAnnotation {

}

下面的Person物件使用了自定義的MethodAnnotation。

public class Person {

    public void eat() {
        System.out.println("eating");
    }

    @MethodAnnotation
    public void walk() {
        System.out.print("walking");
    }

}

我們可以通過反射獲取Annotation的資訊。

 Class<Person> personClass = Person.class;
        Method[] methods = personClass.getMethods();
        for(Method method : methods){
            if (method.isAnnotationPresent(MethodAnnotation.class)){
                method.invoke(personClass.newInstance());
            }
        }

輸出:

walking

我們也可以給自定義的Annotation加方法。

@Target(ElementType.TYPE)
public @interface personAnnotation {
    int id() default 1;
    String name() default "bowen";
}

下面是對personAnnotation的使用。

@personAnnotation(id = 8, name = "john")
public class Person {

    public void eat() {
        System.out.println("eating");
    }

    @MethodAnnotation
    public void walk() {
        System.out.print("walking");
    }

}

Annotation是如何被處理的

當Java原始碼被編譯時,編譯器的一個外掛annotation處理器則會處理這些annotation。處理器可以產生報告資訊,或者建立附加的Java原始檔或資源。如果annotation本身被加上了RententionPolicy的執行時類,則Java編譯器則會將annotation的後設資料儲存到class檔案中。然後,Java虛擬機器或其他的程式可以查詢這些後設資料並做相應的處理。

當然除了annotation處理器可以處理annotation外,我們也可以使用反射自己來處理annotation。Java SE 5有一個名為AnnotatedElement的介面,Java的反射物件類Class,Constructor,Field,Method以及Package都實現了這個介面。這個介面用來表示當前執行在Java虛擬機器中的被加上了annotation的程式元素。通過這個介面可以使用反射讀取annotation。AnnotatedElement介面可以訪問被加上RUNTIME標記的annotation,相應的方法有getAnnotation,getAnnotations,isAnnotationPresent。由於Annotation型別被編譯和儲存在二進位制檔案中就像class一樣,所以可以像查詢普通的Java物件一樣查詢這些方法返回的Annotation。

Annotation的廣泛使用

Annotation被廣泛用於各種框架和庫中,下面就列舉一些典型的應用.

Junit

Junit是非常著名的一款單元測試框架,使用Junit的時候需要接觸大量的annotation。

  • @Runwith 自定義測試類的Runner
  • @ContextConfiguration 設定Spring的ApplicationContext
  • @DirtiesContext 當執行下一個測試前重新載入ApplicationContext.
  • @Before 呼叫測試方法前初始化
  • @After 呼叫測試方法後處理
  • @Test 表明該方法是測試方法
  • @Ignore 可以加在測試類或測試方法上,忽略執行。
  • @BeforeClass:在該測試類中的所有測試方法執行前呼叫,只被呼叫一次(被標註的方法必須是static)
  • @AfterClass:在該測試類中的所有的測試方法執行完後呼叫,只被執行一次(被標註的方法必須是static)

Spring

Spring 號稱配置地獄,Annotation也不少。

  • @Service 給service類加註解
  • @Repository 給DAO類加註解
  • @Component 給元件類加註解
  • @Autowired 讓Spring自動裝配bean
  • @Transactional 配置事物
  • @Scope 配置物件存活範圍
  • @Controller 給控制器類加註解
  • @RequestMapping url路徑對映
  • @PathVariable 將方法引數對映到路徑
  • @RequestParam 將請求引數繫結到方法變數
  • @ModelAttribute 與model繫結
  • @SessionAttributes 設定到session屬性

Hibernate

  • @Entity 修飾entity bean
  • @Table 將entity類與資料庫中的table對映起來
  • @Column 對映列
  • @Id 對映id
  • @GeneratedValue 該欄位是自增長的
  • @Version 版本控制或併發性控制
  • @OrderBy 排序規則
  • @Lob 大物件標註

Hibernate還有大量的關於聯合的annotation和繼承的annotation,這裡就不意義列舉了。

JSR 303 – Bean Validation

JSR 303 – Bean Validation是一個資料驗證的規範,其對Java bean的驗證主要通過Java annotation來實現。

  • @Null被註釋的元素必須為 null
  • @NotNull被註釋的元素必須不為 null
  • @AssertTrue被註釋的元素必須為 true@AssertFalse被註釋的元素必須為 false@Min(value)被註釋的元素必須是一個數字,其值必須大於等於指定的最小值
  • @Max(value)被註釋的元素必須是一個數字,其值必須小於等於指定的最大值
  • @DecimalMin(value)被註釋的元素必須是一個數字,其值必須大於等於指定的最小值
  • @DecimalMax(value)被註釋的元素必須是一個數字,其值必須小於等於指定的最大值
  • @Size(max, min)被註釋的元素的大小必須在指定的範圍內
  • @Digits (integer, fraction)被註釋的元素必須是一個數字,其值必須在可接受的範圍內
  • @Past被註釋的元素必須是一個過去的日期
  • @Future被註釋的元素必須是一個將來的日期
  • @Pattern(value)被註釋的元素必須符合指定的正規表示式

其實還有很多使用了annotaion的framework或library,這裡就不一一列舉了,希望大家能舉一反三,深入瞭解Java中的annotation。

相關文章