java元注解:Target、Retention、Documented、Inherited、Repeatable
- 一、元注解
-
- 1.什麼是元注解
- 2.元注解的作用
- 二、五個元注解
-
- 1.Target
-
- 1.1 Target詳解
- 1.2 Target使用舉例
- 2.Retention
-
- 2.1 Retention詳解
- 2.2 Retention使用舉例
- 3.Documented
-
- 3.1 Documented詳解
- 3.2 Documented使用舉例
- 4.Inherited
-
- 4.1 Inherited詳解
- 4.2 Inherited使用舉例
- 5.Repeatable注解
-
- 5.1 Repeatable詳解
- 5.2 Repeatable使用舉例
- 三、總結
前言:
在使用SpringBoot建構項目時,項目的啟動類中都會有這個注解:SpringBootApplication,檢視這個注解的實作我們可以發現這個注解類的實作也使用了很多其他的注解,其中有四個是java提供的注解他們是:Target、Retention、Documented、Inherited。他們就是元注解。
一、元注解
1.什麼是元注解
java從1.5開始提供注解的使用,于此同時提供了四個元注解Target、Retention、Documented、Inherited,在java8時又新增了一個元注解Repeatable,到此元注解便有5個了。那什麼是元注解呢,我們知道java是支援自定義注解的,所謂元注解就是用來修飾注解類的注解。為什麼要用元注解來修飾注解類呢?因為我們總需要一個标志來表明這個注解的一些特性,以及為這個注解類加上一些辨別以便在jvm執行被注解的類時來解析注解的意思。事實上被元注解修飾的類預設都會繼承Annotation接口。
2.元注解的作用
首先我們需要知道,無論是元注解還是java提供的原生注解亦或者是我們自定義的注解,他們的本質都是一個接口,接口裡面提供了屬性或者待實作的方法,那我們怎麼實作讓這個接口變成jvm認識的注解類型呢,其實就是通過元注解來實作的,我們通過元注解來标注目前注解的作用範圍,以及生效階段等等。比如Target這個元注解用來标注,目前注解類可以使用的位置如方法、類、域、包等等。使用Retention注解來标注保留政策,或者叫生命周期也可以等等。這兩個基本也是自定義注解都需要使用的注解。
二、五個元注解
每個SpringBoot項目都有個啟動類,啟動類上都必然會有一個注解:@SpringBootApplication,點進去這個注解的實作就會發現,這個注解的實作便使用了四個最初的元注解,那他們具體的作用是什麼呢?
1.Target
元注解Target用來标注注解類(元注解是被用來修飾注解類的,這裡說的注解類是指被元注解修飾的注解類)的作用目标,也可以叫标注注解類的使用場所。通過ElementType枚舉類來表示該注解類的作用目标,如果是ElementType.TYPE則表示這個注解類可以使用在類、接口、枚舉等類型上面,ElementType還有很多枚舉,下面就來詳細看下這些枚舉都代表了什麼。
1.1 Target詳解
下面列出了ElementType枚舉類的源碼,共有10種類型,其中前8中是1.5便已經存在,最後兩種是1.8開始加入的,前面幾種應用場景都比較多,最後新加的兩種目前筆者還沒有碰到使用場景,筆者提供了漢語釋義以幫助了解。
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
// 可以作用在:類、接口、枚舉類上
TYPE,
/** Field declaration (includes enum constants) */
// 可以作用在:域(示例變量)上
FIELD,
/** Method declaration */
// 可以作用在:方法上
METHOD,
/** Formal parameter declaration */
// 可以作用在:方法參數上(方法接收參數時的變量,比如@RequestBody注解)
PARAMETER,
/** Constructor declaration */
// 可以作用在:構造器上
CONSTRUCTOR,
/** Local variable declaration */
// 可以作用在:局部變量上
LOCAL_VARIABLE,
/** Annotation type declaration */
// 可以作用在:注解類型上
ANNOTATION_TYPE,
/** Package declaration */
// 可以作用在:包上
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
// 可以作用在:類型參數上,jdk1.8時新增
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
// 可以作用在:使用類型的任何地方,jdk1.8時新增
TYPE_USE
}
1.2 Target使用舉例
下面列舉出幾種ElementType枚舉類型以及對應的使用場景,若是一個注解類支援多個地方使用,則使用逗号将多個枚舉類型隔開即可。比如@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER})
- ElementType.TYPE :@SpringBootApplication
- ElementType.FIELD :@Autowired
- ElementType.METHOD :@PostMapping
- ElementType.PARAMETER :@RequestBody
- ElementType.CONSTRUCTOR :@Autowired
- ElementType.ANNOTATION_TYPE :@Documented
2.Retention
Retention的字面意思是保留,該注解的作用其實也是保留。Retention注解作用是用以标注注解類(元注解是被用來修飾注解類的,這裡說的注解類是指被元注解修飾的注解類)的保留政策,有些人喜歡叫标注注解類的生命周期,其實這些形容都是可以的,主要是了解就行。那怎麼标注注解類的保留政策呢,它也是通過枚舉類型來實作聲明保留政策的,比如RetentionPolicy.RUNTIME就代表會将将該注解類保留到到運作期間。下面一起看下所有的保留政策。
2.1 Retention詳解
下面列出了RetentionPolicy 枚舉類的源碼,在該枚舉類中有三個枚舉類型,他們分别代表的意思筆者已經翻譯了出來,以供參考,簡而言之便是SOURCE代表隻在源碼中有效,CLASS代表在編譯器有效,RUNTIME代表會在運作期有效。從SOURCE到RUNTIME他們的效用是遞增的。
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
}
2.2 Retention使用舉例
下面列舉了RetentionPolicy 中幾種枚舉類型的使用場景,這些注解大部分開發中都使用過,是以可以幫助我們了解這些元注解的作用,比如dubbo 2.7.7開始使用的DubboReference注解,以及java自己提供的方法重寫注解Override。
- RetentionPolicy.RUNTIME:@DubboReference
- RetentionPolicy.SOURCE:@Override
3.Documented
Document注解與Target和Retention功能點不同,他的使用無需與枚舉類配合,直接在需要注解的地方打上注解即可。功能上前面兩個注解都與代碼的編譯有關,Documented的作用隻有一點:在使用javadoc生成api文檔時會将使用Documented注解類作用的類上加上注解類的資訊。詳細我們來看下Documented的源碼注釋。
3.1 Documented詳解
下面展示了Documented的源碼,源碼裡有這麼一段注釋:If a type declaration is annotated with Documented, its annotations become part of the public API of the annotated elements.這段翻譯起來不是太費勁還是容易看懂的,意思是:如果一個類型被Documented注解聲明了,那麼他的所有注解會變成公共api注解的一部分。就是會在api文檔裡面可見,這也就是Documented的作用了。
/**
* Indicates that annotations with a type are to be documented by javadoc
* and similar tools by default. This type should be used to annotate the
* declarations of types whose annotations affect the use of annotated
* elements by their clients. If a type declaration is annotated with
* Documented, its annotations become part of the public API
* of the annotated elements.
* * @author Joshua Bloch
* @since 1.5
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
3.2 Documented使用舉例
下面列舉出幾處使用了Documented注解的地方,供參考。
- Documented:@SpringBootApplication
- Documented:@SpringBootConfiguration
- Documented:@EnableAutoConfiguration
4.Inherited
Inherited的意思是繼承的,在注解中的作用也是和繼承息息相關的,使用的話和Documented類似,也是無需枚舉類的配合隻需要在需要使用的地方直接打上注解即可,那Inherited注解到底有什麼用呢,一起看下他的源碼好了。
4.1 Inherited詳解
/**
* Indicates that an annotation type is automatically inherited. If
* an Inherited meta-annotation is present on an annotation type
* declaration, and the user queries the annotation type on a class
* declaration, and the class declaration has no annotation for this type,
* then the class's superclass will automatically be queried for the
* annotation type. This process will be repeated until an annotation for this
* type is found, or the top of the class hierarchy (Object)
* is reached. If no superclass has an annotation for this type, then
* the query will indicate that the class in question has no such annotation.
* * <p>Note that this meta-annotation type has no effect if the annotated
* type is used to annotate anything other than a class. Note also
* that this meta-annotation only causes annotations to be inherited
* from superclasses; annotations on implemented interfaces have no
* effect.
* * @author Joshua Bloch
* @since 1.5
* @jls 9.6.3.3 @Inherited
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
上面展示了Inherited的源碼,這注釋是真的長,筆者英語功底是真的一般,隻能借助翻譯軟體了。我們來看下第一段的翻譯結果:
訓示自動繼承注釋類型。如果
繼承的元注釋出現在注釋類型上
聲明,然後使用者查詢類上的注釋類型
聲明,并且類聲明沒有對此類型的注釋,
類的父類将自動被查詢
注釋類型。這個過程将重複進行,直到對此進行注釋為止
類型,或類層次結構的頂層(Object)
是達到了。如果沒有超類具有此類型的注釋,則
查詢将表明所涉及的類沒有這樣的注釋。
可以看到翻譯軟體翻譯的還是比較清晰的,大緻意思就是若是有類型上出現了繼承過來的Inherited注解,則會去查詢他的父類是否擁有Inherited注解,父類沒有則查詢父類的父類直到Object類型。從這段話可以發現Innheried注解是可以被繼承的,此外查詢機制有些類似于雙親委派模型,都會一直向上操作。再來看一看第二段的解釋
請注意,如果被注釋的類型用于注釋類以外的任何内容,則此元注釋類型将不起作用。還要注意,這個元注釋隻會導緻注釋從父類繼承;已實作接口上的注釋不起作用
這段主要就是補充了該注解的應用場景,說明該注解隻能使用在注解類上,其他場景使用時沒有效果的,也就是說隻能使用在注解類上,然後被注解類修飾的類的子類将自動繼承該注解。這也就是Inherited的作用了。
4.2 Inherited使用舉例
下面列舉出幾處使用了Inherited注解的地方,供參考:可以發現我們常用的Controller、ResponseBody、RequestMapping等都是沒有Inherited注解的,根據Inherited注解的特性可以發現一旦使用了Inherited注解, Controller、ResponseBody、RequestMapping修飾的類的子類也相當于擁有了Controller、ResponseBody、RequestMapping,這樣就會不太友好。
- Inherited:SpringBootApplication
5.Repeatable注解
這個注解是JDK1.8(也可以叫JDK8)時新增加的元注解,Repeat字面意思是重複的,這個元注解的作用其實和重複的也是息息相關的。詳細的解析我們看下Repeatable的源碼。
5.1 Repeatable詳解
下面展示了Repeatable的源碼,從源碼中的注釋我們可以看到這段話:The value of {@code @Repeatable} indicates the containing annotation type for the repeatable annotation type.意思是:包含該元注解的注解支援重複使用。我們知道如果我們在一個位置加兩個@RestController注解,那肯定會報錯,但是如果@RestController的實作上有了Repeatable就不會報錯了。他的作用就是這個。
/**
* The annotation type {@code java.lang.annotation.Repeatable} is
* used to indicate that the annotation type whose declaration it
* (meta-)annotates is <em>repeatable</em>. The value of
* {@code @Repeatable} indicates the <em>containing annotation
* type</em> for the repeatable annotation type.
*
* @since 1.8
* @jls 9.6 Annotation Types
* @jls 9.7 Annotations
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
/**
* Indicates the <em>containing annotation type</em> for the
* repeatable annotation type.
* @return the containing annotation type
*/
Class<? extends Annotation> value();
}
5.2 Repeatable使用舉例
Repeatable的使用筆者還沒有碰到,因為Repeatable是JDK1.8後加入的,是以目前應用與前四個相比并不是太廣。若是想要實戰下Repeatable的使用,筆者建議看下這篇文章他寫的還是挺好的:檢視Repeatable的實戰。
三、總結
java中的元注解便是用來修飾注解類的,這也是他們名稱的由來。meta-annotaion。這裡介紹了五種java元注解,筆者也是做一個總結,希望可以對路過的你有所幫助。