咱們在平時的開發過程當中看到不少如@Override,@SuppressWarnings,@Test等樣式的代碼就是註解,註解是放到類、構造器、方法、屬性、參數前的標記。java
給某個類、方法..添加了一個註解,這個環節僅僅是作了一個標記,對代碼自己並不會形成任何影響,須要後續環節的配合,須要其餘方法對該註解賦予業務邏輯處理。就如同咱們在微信上發了一個共享定位,此時並無什麼用,只有當後面其餘人都進入了這個共享定位,你們之間的距離才能明確,才知道該怎麼聚在一塊兒。程序員
註解分爲三類:後端
如@Override,@SuppressWarnings都是編譯器使用到的註解,做用是告訴編譯器一些事情,而不會進入編譯後的.class文件。微信
@Override:告訴編譯器檢查一下是否重寫了父類的方法;app
@SuppressWarnings:告訴編譯器忽略該段代碼產生的警告;框架
對於開發人員來講,都是直接使用,無需進行其餘操做前後端分離
須要經過工具對.class字節碼文件進行修改的一些註解,某些工具會在類加載的時候,動態修改用某註解標註的.class文件,從而實現一些特殊的功能,一次性處理完成後,並不會存在於內存中,都是很是底層的工具庫、框架會使用,對於開發人員來講,通常不會涉及到。ide
一直存在於JVM中,在運行期間能夠讀取的註解,也是最經常使用的註解,如Spring的@Controller,@Service,@Repository,@AutoWired,Mybatis的@Mapper,Junit的@Test等,這類註解不少都是工具框架自定義在運行期間發揮特殊做用的註解,通常開發人員也能夠自定義這類註解。微服務
咱們使用@interface來定義一個註解工具
/** * 定義一個Table註解 */ public @interface Table { String value() default ""; } /** * 定義一個Colum註解 */ public @interface Colum { String value() default ""; String name() default ""; String dictType() default ""; }
這樣就簡單地將一個註解定義好了
咱們上面定義的註解主要用到了String類型,但實際上還能夠是基本數據類型(不能爲包裝類)、枚舉類型。
註解也有一個約定俗成的東西,最經常使用的參數應該命名爲value,同時通常狀況下咱們都會經過default參數設置一個默認值。
但這樣是否是就知足於咱們的使用了呢,我想把@Table
註解僅用於類上,@Colum
註解僅用於屬性上,怎麼辦?並且開始提到的三類註解,通常開發人員用的都是運行期的註解,那咱們定義的是嗎?
要回答這些問題,就須要引入一個概念「元註解」。
能夠修飾註解的註解即爲元註解,Java已經定義了一些元註解,咱們能夠直接使用。
顧名思義指定註解使用的目標對象,參數爲ElementType[]
public @interface Target { /** * Returns an array of the kinds of elements an annotation type * can be applied to. * @return an array of the kinds of elements an annotation type * can be applied to */ ElementType[] value(); }
而下面是ElementType枚舉中定義的屬性,不設置Target的時候,除了TYPE_PARAMETER,TYPE_USE,其餘地方都至關於配置上了。
public enum ElementType { /** 經過ElementType.TYPE能夠修飾類、接口、枚舉 */ TYPE, /** 經過ElementType.FIELD能夠修飾類屬性 */ FIELD, /** 經過ElementType.METHOD能夠修飾方法 */ METHOD, /** 經過ElementType.PARAMETER能夠修飾參數(如構造器或者方法中的) */ PARAMETER, /** 經過ElementType.CONSTRUCTOR能夠修改構造器 */ CONSTRUCTOR, /** 經過ElementType.LOCAL_VARIABLE能夠修飾方法內部的局部變量 */ LOCAL_VARIABLE, /** 經過ElementType.ANNOTATION_TYPE能夠修飾註解 */ ANNOTATION_TYPE, /** 經過ElementType.PACKAGE能夠修飾包 */ PACKAGE, /** * 能夠用在Type的聲明式前 * * @since 1.8 */ TYPE_PARAMETER, /** * 能夠用在全部使用Type的地方(如泛型、類型轉換等) * * @since 1.8 */ TYPE_USE }
咱們主要說一下ElementType.PACKAGE和1.8添加的ElementType.TYPE_PARAMETER和ElementType.TYPE_USE
ElementType.PACKAGE
@Target(ElementType.PACKAGE) public @interface Table { String value() default ""; }
含義是用來修飾包,但咱們用來修飾包的時候卻提示錯誤
咱們按照提示建立package-info.java文件,這裏須要注意一下,經過IDE 進行new --> Java Class是建立不了的,須要經過new File文件建立
@Table package annotation; class PackageInfo { public void hello() { System.out.println("hello"); } }
ElementType.TYPE_PARAMETER和ElementType.TYPE_USE
這兩個一塊兒說,由於它們有類似之處。都是Java1.8後添加的
@Target(ElementType.TYPE_USE) public @interface NoneEmpty { String value() default ""; } @Target(ElementType.TYPE_PARAMETER) public @interface NoneBlank { String value() default ""; }
很明顯使用ElementType.TYPE_PARMETER修飾的註解@NoneBlank沒法在泛型使用的時候編譯經過,僅能用於類的泛型聲明,而經過ElementType.TYPE_USE修飾的註解@NoneEmpty能夠。
能夠用於定義註解的生命週期,參數爲枚舉RetentionPolicy,包括了SOURCE,CLASS,RUNTIME
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { /** * Returns the retention policy. * @return the retention policy */ RetentionPolicy value(); } public enum RetentionPolicy { /** * 僅存在於源代碼中,編譯階段會被丟棄,不會包含於class字節碼文件中. */ SOURCE, /** * 【默認策略】,在class字節碼文件中存在,在類加載的時被丟棄,運行時沒法獲取到 */ CLASS, /** * 始終不會丟棄,可使用反射得到該註解的信息。自定義的註解最經常使用的使用方式。 */ RUNTIME }
表示是否將此註解的相關信息添加到javadoc文檔中
定義該註解和子類的關係,使用此註解聲明出來的自定義註解,在使用在類上面時,子類會自動繼承此註解,不然,子類不會繼承此註解。注意,使用@Inherited聲明出來的註解,只有在類上使用時纔會有效,對方法,屬性等其餘無效。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface Person { String value() default "man"; } @Person public class Parent { } //子類也擁有@Person註解 class Son extends Parent { }
用@interface定義註解
能夠添加多個參數,核心參數按約定用value,爲每一個參數能夠設置默認值,參數類型包括基本類型、String和枚舉
可使用元註解來修飾註解,元註解包括多個,必須設置@Target
和@Retention
,@Retention
通常設置爲RUNTIME
。
咱們前面已經提到光配置了註解,其實沒有做用,須要經過相應的代碼來實現該註解想要表達的邏輯。
註解定義後也是一種class,全部的註解都繼承自java.lang.annotation.Annotation
,所以,讀取註解,須要使用反射API。
//定義的註解 @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Colum { String value() default ""; //用於表示某個屬性表明的中文含義 String name() default ""; }
用註解@Colum來修飾某個類的屬性
public class Person { @Colum(name = "姓名") private String name; @Colum(name = "性別") private String gender; @Colum(name = "年齡") private int age; @Colum(name = "住址") private String address; public String getName() {return name;} public void setName(String name) {this.name = name;} public String getGender() {return gender;} public void setGender(String gender) {this.gender = gender;} public int getAge() {return age;} public void setAge(int age) {this.age = age;} public String getAddress() {return address;} public void setAddress(String address) {this.address = address;} }
經過反射讀取這個類的全部字段的中文含義,並保存到list中,而後打印出來
public static void main(String[] args) throws ClassNotFoundException { List<String> columNames = new ArrayList<>(); Class clazz = Class.forName("annotation.Person"); //獲取Person類全部屬性 Field[] fields = clazz.getDeclaredFields(); for (Field field : fields){ //獲取該屬性的Colum註解 Colum colum = field.getAnnotation(Colum.class); //或者能夠先判斷有無該註解 field.isAnnotationPresent(Colum.class); //將該屬性經過註解配置好的中文含義取出來放到集合中 columNames.add(colum.name()); } //打印集合 columNames.forEach((columName) -> System.out.println(columName)); }
結果以下:
姓名 性別 年齡 住址
好比咱們有一些常見的應用場景,須要把網站上的列表導出成excel表格,咱們經過註解的方式把列名配置好,再經過反射讀取實體須要導出(是否須要導出,也可經過註解配置)的每一個字段的值,從而實現excel導出的組件。
本文只是拋磚引玉地講解了註解的基本概念,註解的做用,幾種元註解的功用以及使用方法,並經過一個簡單的例子講解了一下註解的處理,並不全面,文中經過Field講解了註解的基本Api,但註解還能夠修飾類、構造器、方法等,也有相對應的註解處理方法,你們可自行查一下API手冊相關內容,大同小異,有不對之處,請批評指正,望共同進步,謝謝!