[轉帖]Java高級系列——注解(Annotations)


Java高級系列——注解(Annotations)

一、介紹

本系列文章的這一部分我們將會介紹Java 5版本引入的除泛型和枚舉之外的另外一個強大特性:注解,可以將注解看成一種特殊的接口。

注解是一種特殊種類的元數據,它能夠關聯Java語言中不同元素和結構。有意思的是,在Java生態系統中大多數使用樣板XML描述符的地方,注解在消除這些XML描述符上做出了很大的貢獻。注解引入了新的,類型安全以及非常強健的配置和個性化技術。

二、注解作為特殊接口

就像我們在前文中所提到的一樣,注解用來關聯Java語言中的元數據和不同的元素。

注解本身對它所注解的元素不會造成任何直接的影響。但是,依靠注解和它的定義方式,它們可以被Java編譯器(注解最好的實例就是我們前面的文章中所使用的@Override注解)、注解處理器和運行時代碼使用反射和其他的虛擬機內省技術使用。

讓我們來看一個最簡單的注解聲明:

public @interface SimpleAnnotation {
}

 

@interface關鍵字引入了新的注解類型,這也是為何注解可以被當做專門的接口看待,注解可以聲明有默認值和沒有默認值的屬性,比如:

public @interface SimpleAnnotationWithAttributes {
    String name();
    int order() default 0;
}

 

如果注解聲明了沒有默認值的屬性,那么在該注解被應用的所用地方都應該提供注解屬性值。

@SimpleAnnotationWithAttributes(name = "new annotation")

 

為了方便,如果注解只有一個屬性並且屬性的名稱是value,那么屬性的名稱就可以被省略,比如:

public @interface SimpleAnnotationWithValue {
    String value();
}

 

上面聲明的這個注解就可以按照如下的方式去使用:

@SimpleAnnotationWithValue("new annotation")

  

注解也有一些限制,在某些情況下使用注解可能會不太方便。

  • 首先,注解不支持任何繼承:注解不能繼承其他的注解。

  • 其次,不能通過編碼的形式使用new關鍵字創建注解實例。

  • 第三,注解只能聲明基本數據類型屬性,String或者 Class<?>類型及其數組。

  • 第四,注解中不允許聲明方法和構造器。

三、注解及其保留策略(Retention Policy)

每個注解都有一個被稱為保留策略(Retention Policy)的特征,它是一組如何保留注解的策略組合的枚舉(RetentionPolicy類型)。保留策略可以設置為以下的值之一。

策略 描述
CLASS 注解被編譯器記錄在class文件中,但是在運行時不需要虛擬機保留(即運行時不存在)
RUNTIME 注解被編譯器記錄在class文件中並且在運行時被虛擬機保留,因此可以通過反射機制獲取
SOURCE 注解被編譯器丟棄(即注解僅在源碼中保留,class文件中不存在)

保留策略對注解何時可用於處理有至關重要的影響。保留策略可以通過使用@Retention注解來設置。比如:

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention( RetentionPolicy.RUNTIME )
public @interface AnnotationWithRetention {
}

  

設置注解的保留策略為RUNTIME將會保證注解在編譯過程和運行的應用程序中存在。

四、注解以及元素類型(ElementType)

注解的另外一個特征就是每個注解必須有它能夠應用的元素類型。有點類似於保留策略,元素類型被定義成一組可能的元素類型的枚舉(ElementType)。

元素類型 描述
ANNOTATION_TYPE 標明注解可用於注解類型聲明(應用於另外的注解)
CONSTRUCTOR 標明注解可用於構造函數聲明
FIELD 標明注解可用於字段/域(包括枚舉常量)聲明
LOCAL_VARIABLE 標明注解可用於局部變量聲明
METHOD 標明注解可用於方法聲明
PACKAGE 標明注解可用於包聲明
PARAMETER 標明注解可用於參數聲明
TYPE 標明注解可用於類、接口(包括注解類型)、枚舉類型的聲明

此外,除了上面所描述的這些元素類型之外,Java 8版本引入了兩個新的注解可以使用的元素類型。

元素類型 描述
TYPE_PARAMETER 標明注解可以寫在類型變量的聲明語句中
TYPE_USE 表示該注解能寫在使用類型的任何語句中(eg:聲明語句、泛型和強制轉換語句中的類型)

和保留策略對比,注解可以使用@Target注解聲明多個與之相關聯的多個元素類型。比如:

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target({ElementType.FIELD, ElementType.METHOD})
public @interface AnnotationWithTarget {
}

 

 

大多數情況下,您要創建的所有注解都應該同時指定保留策略和元素類型才能有用。

五、注解與繼承(Annotations and inheritance)

在Java中,聲明注解與繼承之間存在非常重要的聯系。默認情況下,子類不能夠繼承父類中聲明的注解,但是,有一種方法就是可以通過使用@Inherited注解在類層次結構中傳遞指定的注解。比如:

@Target( { ElementType.TYPE } )
@Retention( RetentionPolicy.RUNTIME )
@Inherited
@interface InheritableAnnotation {
}

@InheritableAnnotation
public class Parent {
}

public class Child extends Parent {
}

 

在這個例子中,被聲明在父類中的@InheritableAnnotation注解可以被子類繼承。

六、可重復注解(Repeatable annotations)

我們到目前為止還沒有討論過Java 8之前的版本和注解相關的另外的一個限制,即相同的注解在同一個地方只能出現一次,不能重復多次。Java 8通過提供可重復注解的支持放松了這種限制。比如:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RepeatableAnnotations {
RepeatableAnnotation[] value();
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable( RepeatableAnnotations.class )
public @interface RepeatableAnnotation {
    String value();
};

@RepeatableAnnotation("repeatition 1")
@RepeatableAnnotation("repeatition 2")
public void performAction() {
    // Some code here
}

 

 

雖然在Java 8中可重復注解功能需要做一些工作才能使注解可重復使用(使用@Repeatable),但最終的結果是值得的:更簡潔和緊湊的注釋代碼。

七、注解處理器(Annotation processors)

Java編譯器支持一種稱為注解處理器的特殊類型的插件(使用-processor命令行參數),它可以在編譯階段處理注解。 注解處理器可以分析注解的作用(執行靜態代碼分析),創建額外的Java源文件或資源(可以被編譯和處理),或者改變注解的代碼。

在告知編譯器那個注解可以被注解處理器應用和處理,保留策略(Retention Policy)扮演了非常關鍵的角色。

注釋處理器被廣泛使用,但是編寫一個注解處理器需要一些關於Java編譯器如何工作和編譯過程本身的知識。

八、注解與配置大於約定(Annotations and configuration over convention)

約定大於配置是一種軟件設計慣例,這種慣例旨在簡化開發過程,但是開發者需要遵循一些簡單的規則(或約定)。比如說,一些MVC框架遵循約定將控制器放在controller目錄(或者包)中,一些ORM框架經常遵循約定在model目錄查找實體類並從各自的類中獲取關系表名稱。

另一方面,注解開創了一種不同的設計慣例,即一切基於明確的配置。就我們上面所說的例子而論,@Controller注解可以明確的標記任何類作為控制器,@Entity注解可以關聯數據庫表。注解的好處也來自於這樣的事實,即注解的可拓展性、可擁有額外屬性和有限的特定元素類型。Java編譯器會強制執行不當的注解使用並在編譯階段很早就發現錯誤配置問題。

九、何時使用注解

注解實際上無處不在:Java標准庫有很多,基本上每個Java規范都包含注解。 每當您需要將附加的元數據與您的代碼相關聯,注解都是簡單直接的方法。

有趣的是,Java社區正在不斷努力開發公共通用語義概念並通過多種技術標准(更多信息,請參閱JSR-250規范)化注解。目前,標准Java庫中包含以下注解。

注解 描述
@Deprecated 表明被標記元素已過期並應該不再使用。每當程序使用被該注解注解的方法、類或字段(域)時,編譯器會生成警告。
@Override 提示編譯器該元素是為了覆蓋在父類中聲明的元素。
@SuppressWarnings 指示編譯器抑制通過其他方式生成的警告。
@SafeVarargs 當應用於方法或構造函數時,聲明代碼不會對其可變參數執行潛在的不安全操作。使用此注解類型時,與可變參數使用有關的未經檢查的警告將被抑制。
@Retention 指定如何保留被標記的注解。
@Target 指定被標記注解可以使用那些Java元素
@Documented 表明使用該注解的元素可以使用Javadoc工具記錄(默認情況下,注解是不包含在Javadoc中的)
@Inherited 表明注解類型可以從父類繼承

Java 8版本的發布增加了也增加了幾個新的注解。

注解 描述
@FunctionalInterface 表示類型聲明是按照Java語言規范定義的函數接口。
@Repeatable 表明被標記的注解可以在相同的地方應用多次。

十、小結

本文我們大概講述了一些和注解相關內容,通過本文的閱讀我們大致可以明白注解可以作為一個特殊的接口,在定義一個新的注解是,我們需要指定注解的保留策略以及元素類型。同時我們也闡述了如何讓子類繼承父類的注解以及如何聲明可重復注解。當然我們也講述了一些關於注解處理器以及在注解所遵從的配置大於約定的概念。接下來我們將會討論如何有效的編寫方法,敬請期待。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com