本文目的:項目開發過程當中遇到自定義註解,想要弄清楚其原理,可是本身的基礎知識不足以支撐本身去探索此問題,因此先記錄問題,而後補充基礎知識,而後解決其問題。記錄此學習過程。html
//使用註解的地方 @ServiceScan({"com.sinosoft.lis.pubfun"}) public class CodeQuerySQL {} //註解類 ServiceScan @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface ServiceScan { String[] value() default {}; } //這個com.sinosoft.lis.pubfun包下的類 @CodeQuery public interface CodeQuery_Framework {} //註解類CodeQuery @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface CodeQuery { }
問題描述: 開發中,咱們須要本身新建一個codequeryframework_nb類,這個類是這樣使用的,放在com.sinosoft.lis.pubfun包下就行,而後自定義方法,就會自動被掃描到,而後會自動加載定義的接口方法類,去實現咱們的查詢下拉的功能。註解使用正確,包放在正確的位置就可使用了,可是爲何不會和以前的codequeryframework衝突?具體是怎麼實現的,咱們組內的成員都沒搞明白。我決定把註解這個知識點往深處挖一挖。java
咱們的問題:程序員
學習目的:spring
學習過程:sql
其餘人提到的知識點:java5,元註解,自定義註解,註解的實現,註解的屬性,註解的做用,在反射中使用註解數組
//「java.lang.annotation.Annotation」接口中有這麼一句話,用來描述『註解』。 The common interface extended by all annotation types 全部的註解類型都繼承自這個普通的接口(Annotation)
咱們先隨便點開一個JDK內的註解,查看一下是如何定義的springboot
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
這是註解 @Override 的定義,其實它本質上就是:網絡
public interface Override extends Annotation{ }
沒錯,註解的本質就是一個繼承了 Annotation 接口的接口。有關這一點,你能夠去反編譯任意一個註解類,你會獲得結果的。app
這裏找到了兩位博主的解析,感受簡單易懂很到位。先來一個總體上的認識。框架
在平時不知道咱們是否都用過便利貼,在一張紙上寫好幾句話,貼在咱們須要的地方.還有一個狀況,大多數人都叫咱們程序猿(錢多話少死得快),這也是給咱們貼了一個標籤。像這兩種狀況基本上就是註解。你能夠把這兩種狀況聯想到代碼的註解上。好比咱們定義了一個方法,這個方法要實現加法的運算,那麼咱們就能夠定義一個@ADD標籤。表示這個方法就是實現加法的。咱們程序員一看到這個@ADD,就能很容易理解這個方法是幹嗎的。簡單而言。註解就是對於代碼中某些鮮活個體的貼上去的一張標籤。簡化來說,註解如同一張標籤。由於,若是你以前還未正式的學習過註解,你就能夠把他當成便利貼標籤就行了,這能幫你理解註解的大部份內容。
之前,『XML』是各大框架的青睞者,它以鬆耦合的方式完成了框架中幾乎全部的配置,可是隨着項目愈來愈龐大,『XML』的內容也愈來愈複雜,維護成本變高。因而就有人提出來一種標記式高耦合的配置方式,『註解』。方法上能夠進行註解,類上也能夠註解,字段屬性上也能夠註解,反正幾乎須要配置的地方均可以進行註解。關於『註解』和『XML』兩種不一樣的配置模式,爭論了好多年了,各有各的優劣,註解能夠提供更大的便捷性,易於維護修改,但耦合度高,而 XML 相對於註解則是相反的。追求低耦合就要拋棄高效率,追求效率必然會遇到耦合。本文意再也不辨析二者誰優誰劣,而在於以最簡單的語言介紹註解相關的基本內容。
『元註解』是用於修飾註解的註解,一般用在註解的定義上
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
這是咱們 @Override 註解的定義,你能夠看到其中的 @Target,@Retention 兩個註解就是咱們所謂的『元註解』,『元註解』通常用於指定某個註解生命週期以及做用目標等信息。
//目前jdk官方提供的元註解有4個 @Target:定義註解的做用目標 @Retention:定義註解的生命週期 @Documented:定義註解是否應當被包含在 JavaDoc 文檔中 @Inherited:定義是否容許子類繼承該註解
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) 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(); }
咱們能夠經過如下的方式來爲這個 value 傳值:
@Target(value = {ElementType.FIELD})
被這個 @Target 註解修飾的註解將只能做用在成員字段上,不能用於修飾方法或者類。其中,ElementType 是一個枚舉類型,有如下一些值:
public enum ElementType { /** Class, interface (including annotation type), or enum declaration */ TYPE, //容許被修飾的註解做用在類、接口和枚舉上 /** Field declaration (includes enum constants) */ FIELD, //容許做用在屬性字段上 /** Method declaration */ METHOD, //容許做用在方法上 /** Formal parameter declaration */ PARAMETER, //容許做用在方法參數上 /** Constructor declaration */ CONSTRUCTOR, //容許做用在構造器上 /** Local variable declaration */ LOCAL_VARIABLE, //容許做用在本地局部變量上 /** Annotation type declaration */ ANNOTATION_TYPE, //容許做用在註解上 /** Package declaration */ PACKAGE, //容許做用在包上 /** * Type parameter declaration * 表示該註解能寫在類型變量的聲明語句中(如:泛型聲明)。 * @since 1.8 */ TYPE_PARAMETER, /** * Use of a type * 表示該註解能寫在使用類型的任何語句中。 * @since 1.8 */ TYPE_USE } 注意:上述中文翻譯爲本身翻譯的,若是有錯誤,請自行查閱官方文檔 最後從jdk1.8添加的兩個枚舉類型的做用,是經過搜索網絡資料查詢得來 類型註解: JDK1.8以後,關於元註解@Target的參數類型ElementType枚舉值多了兩個: TYPE_PARAMETER和TYPE_USE。 在Java8以前,註解只能是在聲明的地方所使用,Java8開始,註解能夠應用在任何地方。 ElementType.TYPE_PARAMETER 表示該註解能寫在類型變量的聲明語句中(如:泛型聲明)。 ElementType.TYPE_USE 表示該註解能寫在使用類型的任何語句中。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { /** * Returns the retention policy. * @return the retention policy */ RetentionPolicy value(); }
一樣的,它也有一個 value 屬性:
@Retention(value = RetentionPolicy.RUNTIME
這裏的 RetentionPolicy 依然是一個枚舉類型,它有如下幾個枚舉值可取:
public enum RetentionPolicy { /** * Annotations are to be discarded by the compiler. */ SOURCE, //當前註解編譯期可見,不會寫入 class 文件 /** * 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, //類加載階段丟棄,會寫入 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 //永久保存,能夠反射獲取 }
@Retention 註解指定了被修飾的註解的生命週期,一種是隻能在編譯期可見,編譯後會被丟棄,一種會被編譯器編譯進class文件中,不管是類或是方法,乃至字段,他們都是有屬性表的,而 JAVA 虛擬機也定義了幾種註解屬性表用於存儲註解信息,可是這種可見性不能帶到方法區,類加載時會予以丟棄,最後一種則是永久存在的可見性。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.CLASS) public @interface TestAnnotation { } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface TestAnnotation2 { } @Target(ElementType.TYPE) @Retention(RetentionPolicy.SOURCE) public @interface TestAnnotation3 { }
@TestAnnotation @TestAnnotation2 @TestAnnotation3 public class TestJava { public static void main(String[] args) throws ClassNotFoundException { Class<?> testJava = Class.forName("com.sinosoft.lis.pubfun.TestJava"); Annotation[] annotations = testJava.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation.annotationType()); } }
你明白個人意思吧。
剩下兩種類型的註解咱們平常用的很少,也比較簡單,這裏再也不詳細的進行介紹了,只須要知道他們各自的做用便可.
@Documented 註解修飾的註解,當咱們執行 JavaDoc 文檔打包時會被保存進 doc 文檔,反之將在打包時丟棄.
@Inherited 註解修飾的註解是具備可繼承性的,也就說咱們的註解修飾了一個類,而該類的子類將自動繼承父類的該註解.
#### 除了上述四種元註解外,JDK 還爲咱們預約義了另外三種註解,它們是: 1. @Override 2. @Deprecated 3. @SuppressWarnings
1. @Override 註解想必是你們很熟悉的了,標記爲方法爲重寫,它的定義以下:
/** * Indicates that a method declaration is intended to override a * method declaration in a supertype. If a method is annotated with * this annotation type compilers are required to generate an error * message unless at least one of the following conditions hold: * * <ul><li> * The method does override or implement a method declared in a * supertype. * </li><li> * The method has a signature that is override-equivalent to that of * any public method declared in {@linkplain Object}. * </li></ul> * * @author Peter von der Ahé * @author Joshua Bloch * @jls 9.6.1.4 @Override * @since 1.5 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
它沒有任何的屬性,因此並不能存儲任何其餘信息。它只能做用於方法之上,編譯結束後將被丟棄。因此你看,它就是一種典型的『標記式註解』,僅被編譯器可知,編譯器在對 java 文件進行編譯成字節碼的過程當中,一旦檢測到某個方法上被修飾了該註解,就會去匹對父類中是否具備一個一樣方法簽名的函數,若是不是,天然不能經過編譯。
2. @Deprecated : 主要用來標記該Element已通過時,基本定義以下
/** * A program element annotated @Deprecated is one that programmers * are discouraged from using, typically because it is dangerous, * or because a better alternative exists. Compilers warn when a * deprecated program element is used or overridden in non-deprecated code. * * @author Neal Gafter * @since 1.5 * @jls 9.6.3.6 @Deprecated */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE}) public @interface Deprecated { }
依然是一種『標記式註解』,永久存在,能夠修飾全部的類型,做用是,標記當前的類或者方法或者字段等已經再也不被推薦使用了,可能下一次的 JDK 版本就會刪除。固然,編譯器並不會強制要求你作什麼,只是告訴你 JDK 已經再也不推薦使用當前的方法或者類了,建議你使用某個替代者。
3. @SuppressWarnings:主要用來壓制 java 的警告,它的基本定義以下:
/** * Indicates that the named compiler warnings should be suppressed in the * annotated element (and in all program elements contained in the annotated * element). Note that the set of warnings suppressed in a given element is * a superset of the warnings suppressed in all containing elements. For * example, if you annotate a class to suppress one warning and annotate a * method to suppress another, both warnings will be suppressed in the method. * * <p>As a matter of style, programmers should always use this annotation * on the most deeply nested element where it is effective. If you want to * suppress a warning in a particular method, you should annotate that * method rather than its class. * * @author Josh Bloch * @since 1.5 * @jls 4.8 Raw Types * @jls 4.12.2 Variables of Reference Type * @jls 5.1.9 Unchecked Conversion * @jls 5.5.2 Checked Casts and Unchecked Casts * @jls 9.6.3.5 @SuppressWarnings */ @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { /** * The set of warnings that are to be suppressed by the compiler in the * annotated element. Duplicate names are permitted. The second and * successive occurrences of a name are ignored. The presence of * unrecognized warning names is <i>not</i> an error: Compilers must * ignore any warning names they do not recognize. They are, however, * free to emit a warning if an annotation contains an unrecognized * warning name. * * <p> The string {@code "unchecked"} is used to suppress * unchecked warnings. Compiler vendors should document the * additional warning names they support in conjunction with this * annotation type. They are encouraged to cooperate to ensure * that the same names work across multiple compilers. * @return the set of warnings to be suppressed */ String[] value(); }
它有一個 value 屬性須要你主動的傳值,這個 value 表明一個什麼意思呢,這個 value 表明的就是須要被壓制的警告類型。例如:
public static void main(String[] args) { Date date = new Date(2019, 12, 27); }
這麼一段代碼,程序啓動時編譯器會報一個警告。
Warning:(8, 21) java: java.util.Date 中的 Date(int,int,int) 已過期
而若是咱們不但願程序啓動時,編譯器檢查代碼中過期的方法,就可使用 @SuppressWarnings 註解並給它的 value 屬性傳入一個參數值來壓制編譯器的檢查。
@SuppressWarning(value = "deprecated") public static void main(String[] args) { Date date = new Date(2019, 12, 27); }
這樣你就會發現,編譯器再也不檢查 main 方法下是否有過期的方法調用,也就壓制了編譯器對於這種警告的檢查。
固然,JAVA 中還有不少的警告類型,他們都會對應一個字符串,經過設置 value 屬性的值便可壓制對於這一類警告類型的檢查。
自定義註解的語法比較簡單,經過相似如下的語法便可自定義一個註解。
public @interface InnotationName{ }
固然,自定義註解的時候也能夠選擇性的使用元註解進行修飾,這樣你能夠更加具體的指定你的註解的生命週期、做用範圍等信息。
註解的屬性也叫作成員變量。註解只有成員變量,沒有方法。註解的成員變量在註解的定義中以「無形參的方法」形式來聲明,其方法名定義了該成員變量的名字,其返回值定義了該成員變量的類型。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface TestAnnotation { int id(); String msg(); }
上面代碼定義了 TestAnnotation 這個註解中擁有 id 和 msg 兩個屬性。在使用的時候,咱們應該給它們進行賦值。
賦值的方式是在註解的括號內以 value=」」 形式,多個屬性以前用 ,隔開。
須要注意的是,在註解中定義屬性時它的類型必須是 8 種基本數據類型外加 類、接口、註解及它們的數組。
註解中屬性能夠有默認值,默認值須要用 default 關鍵值指定。好比:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface TestAnnotation { public int id() default -1; public String msg() default "Hi"; }
TestAnnotation 中 id 屬性默認值爲 -1,msg 屬性默認值爲 Hi。 它能夠這樣應用。
@TestAnnotation() public class Test {}
由於有默認值,因此無須要再在 @TestAnnotation 後面的括號裏面進行賦值了,這一步能夠省略。
最後,還須要注意的一種狀況是一個註解沒有任何屬性。好比
public @interface Perform {}
那麼在應用這個註解的時候,括號均可以省略。
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
上述內容咱們介紹了註解使用上的細節,也簡單提到,「註解的本質就是一個繼承了 Annotation 接口的接口」,如今咱們就來從虛擬機的層面看看,註解的本質究竟是什麼。
註解運用的地方太多了,如:
JUnit 這個是一個測試框架,典型使用方法以下:
public class ExampleUnitTest { @Test public void addition_isCorrect() throws Exception { assertEquals(4, 2 + 2); } }
還有例如ssm框架,springboot,springcloud等運用了大量的註解。
算是對註解有了基本的認知。談談自我總結吧。
我以前的問題:
經過反射機制會掃描出全部被@CodeQuery 修飾過的類或者接口並以bean對象的形式注入到本身的容器中來統一管理,根據被@CodeQuery修飾的接口或者類,就能夠肯定了被@CodeQuery修飾過得類都有哪些,遍歷全部Class文件,而後能夠用反射中的Method類來獲取全部被@SQL修飾過的方法的名字,經過方法名字就能夠在程序運行時調用對應的接口來執行sql語句了
Class<TestJava> testJavaClass = TestJava.class; testJavaClass.getAnnotations();