引子:全中國的Java程序員都知道,如今主流的項目架構都是ssm、ssh、springcloud等,而這些框架都離不開spring,而spring中使用了大量的註解(包括spring自定義的註解)。所以想要會用註解,咱們就得知道Java註解的原理和基本用法,這樣有助於咱們在項目中如魚得水。java
在JDK5.0中,新增了不少對如今影響很大的特性,如:枚舉、自動裝箱和拆箱、註解、泛型等等。其中註解的引入,是爲了增長對元數據的支持。程序員
註解:是一個接口,程序能夠經過反射來獲取指定程序元素的Annotation對象,而後經過Annotation對象來取得註解裏的元數據,註解能用來爲程序元素(包、類、方法、成員變量等)設置元數據,它不影響程序代碼的執行。若是但願讓程序中的Annotation在運行時起必定做用,只有經過某種配套的工具對Annotation中的信息進行訪問和處理,這個工具同城爲APT(Annotation program Tool)spring
JDK5.0除了增長註解特性以外,還提供了5個基本的Annotation:編程
- @Overrride:限定重寫父類方法(旨在強制性提醒)
- @Deprecated:表示某個程序元素(類、方法)已過期
- @SuppressWarnings:抑制編譯警告
- @SafeVarargs:堆污染警告
- @FunctionalInterface:指定某個接口必須爲函數式接口 注:函數式接口是指一個接口中只包含一個抽象方法(能夠包含多個默認方法或多個static方法)
以上這幾個註解,在咱們的項目常常可見,可是由於他們對程序只是一個強制提醒或者警告做用,並不影響程序的執行,由於咱們都沒在乎這些註解的做用,而當spring出來以後,大量的註解眼花繚亂,做用各異,但他們都有一個共同做用: 讓咱們在編碼過程當中簡化了很多重複性的代碼。 所以咱們在瞭解了註解的原理以後,必需要能自定義一些註解而且使用它到項目中去,才能讓咱們更好的瞭解和使用它。 而要想自定義註解, 就必須得了解Java提供的幾個元註解 那什麼是元註解呢? 元註解:就是負責註解其它註解的註解json
在Java5以後定義了4個標準的元註解,分別是:架構
target註解用來標識註解所修飾的對象範圍。框架
它的可用範圍(ElementType的取值範圍)有:ssh
CONSTRUCTOR:用於描述構造器(構造方法)ide
FIELD:用於描述域(成員變量)函數
LOCAL_VARIABLE:用於描述局部變量(局部變量)
METHOD:用於描述方法(普通方法)
PACKAGE:用於描述包(包定義)
PARAMETER:用於描述參數(如catch等參數)
TYPE:用於描述類、接口(包括註解類型) 或enum聲明
ANNOTATION_TYPE:用於註解
使用實例:
@Target(ElementType.FIELD)
public @interface TargetTest6{
}
@Target({ElementType.TYPE_PARAMETER,ElementType.METHOD})
public @interface TargetTest7{
}
複製代碼
@Retention定義了該Annotation被保留的時間長短:某些Annotation僅出如今源代碼中,而被編譯器丟棄;而另外一些卻被編譯在class文件中;編譯在class文件中的Annotation可能會被虛擬機忽略,而另外一些在class被裝載時將被讀取(請注意並不影響class的執行,由於Annotation與class在使用上是被分離的)。使用這個meta-Annotation能夠對 Annotation的「生命週期」限制。
它的取值(RetentionPoicy)有:
SOURCE:在源文件中有效(即源文件保留),註解只保留在源代碼中,編譯器直接丟棄這種註解。
CLASS:在class文件中有效(即class保留),編譯器把註解記錄在class文件中,當Java程序運行時,JVM不能獲取該註解的信息。
RUNTIME:在運行時有效(即運行時保留),編譯器將把註解記錄在class文件中,當Java運行時,JVM能夠獲取註解的信息,程序能夠經過反射獲取該註解的信息。
1@Target(ElementType.FIELD) 2@Retention(RetentionPolicy.RUNTIME) 3public @interface TargetTest6{ 4} @Inherited @Inherited註解指定被它休市的註解將具有繼承性:若是莫個類使用了@XXX註解,則其子類自動被@XXX修飾
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface TargetTest6{
}
//全部使用了@TargetTest6註解的類講具有繼承性,
//也就是它的子類自動帶上@TargetTest6註解
複製代碼
@Documented用於指定被該註解修飾的註解類將被javadoc工具提取城文檔.
1@Target(ElementType.FIELD)
2@Retention(RetentionPolicy.RUNTIME)
3@Inherited
4@Documented
5@interface TargetTest6{
6}
7//javadoc工具生成的API文檔將提取@Documented的使用信息
複製代碼
以上就是全部的元註解以及他們的做用分析, 有了這些元註解有什麼用呢? 。。。。 固然是爲了方便咱們在項目中自定義註解咯 那自定義註解怎麼自定義呢? 下面咱們來看看介紹如何自定義註解並利用註解完成一些實際的功能
語法:
類修飾符 @interface 註解名稱{
//成員變量,在註解中以無形參的形式存在
//其方法名和返回值定義了該成員變的名字和類型
String name();
int age();
}
複製代碼
能夠看到:註解的語法和接口的定義很是相似,他也同樣具有有做用域,可是它的成員變量的定義是以無形參的方法形式存在,名字定義了成員變量的名字,返回值定義了變量類型。 下面咱們來自定義一個註解:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AnonTest {
int age();
String name();
}
複製代碼
註解@AnonTest在運行時有效,做用域在成員變量上,它有兩個成員變量分別是int型的age和String型的name。 接下來咱們就可使用這個註解了
public class test {
@AnonTest(age = 0, name = "1")
public Integer tes;
}
複製代碼
這樣就是一個註解的定義和使用了,有人會疑惑說,spring中不少註解都是@xxx,爲何這個@AnonTest必定要要帶上兩個成員變量呢? 緣由很簡單: 註解中的成員變量若是沒有默認值,則在使用註解時必需要給成員變量賦值 但若是成員變量有默認值,那能夠直接在定義註解時,賦值上去,這樣在使用時就能夠省略不寫
@Target(ElementType.FIELD)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface AnonTest {
int age() default 1;
String name() default "註解辣雞";
}
複製代碼
這樣在調用註解時就能夠不賦值
public class test {
@AnonTest
public Integer tes;
}
複製代碼
上面看到,咱們已經使用了註解,可是咱們並沒發現@AnonTest對咱們的tes成員變量有任何做用,這是由於註解自己在程序中是不會生效的,而是須要程序來提取數據而且處理註解本應該作的工做。 所以 咱們須要知道如何從註解中提取信息而且作相應的處理。 Java使用Annotation接口來表明程序元素前面的註解,該接口是全部註解的父接口,該接口主要有如下幾個實現類:
Class:類定義
Constructor:構造器定義
Field:類成員變量定義
Method:類的方法定義
Package:類的包定義
AnnotatedElement接口是全部程序元素所實現的接口的父接口,因此程序經過反射獲取了某個類的AnnotatedElement對象以後,程序就能夠調用該對象的以下幾個方法來訪問註解信息:
getAnnotation(Class annotationClass): 返回改程序元素上存在的、指定類型的註解,若是該類型註解不存在,則返回null。
getAnnotations():返回該程序元素上存在的全部註解。
isAnnotationPresent(Class annotatonClass):判斷該程序元素上是否包含指定類型的註解,存在則返回true,不然返回false.
getDeclaredAnnotations():返回直接存在於此元素上的全部註釋。
複製代碼
有了這些方法,咱們就能夠在類、方法、成員變量等程序元素中獲取到註解信息 好比:
1@Component
2@Service
3public class test {
4
5 @AnonTest
6 public Integer tes;
7
8 @Deprecated
9 public void m1(){
10
11 }
12
13 public static void main(String[] args) {
14 try {
15 //獲取全部test類上的註解
16 Annotation[] ar=Class.forName("com.anon.test").getAnnotations();
17 //獲取全部test類上m1方法的註解
18 Annotation[] ar1=Class.forName("com.anon.test").getMethod("m1").getAnnotations();
19 //遍歷全部的註解,檢測是否有咱們想要的某個註解 如@Component註解
20 for(Annotation a:ar){
21 System.out.println(a.getClass());
22 if(a instanceof Component)
23 System.out.println("用了註解:"+a);
24 }
25 } catch (Exception e) {
26 // TODO Auto-generated catch block
27 e.printStackTrace();
28 }
29 }
30}
31
32//輸出:
33//用了註解:@org.springframework.stereotype.Component(value=)
34//class com.sun.proxy.$Proxy4
複製代碼
利用AnnotatedElement提供的方法能獲取到咱們想要的註解信息以後,就能夠針對註解作一些特定的行爲。
例如,對於全部的註冊用戶信息,系統須要把名稱和年齡上報到另外一個年齡分佈表中,就能夠利用註解就能夠完成這樣的動做
1public class test {
2 @AnonTest(name="張小龍",age=25)
3 public Integer tes;
4 public static void main(String[] args) {
5 test.getInfo(test.class);
6 }
7 public static void getInfo(Class<?> clazz) {
8 //獲取目標類的全部成員變量
9 Field[] fields = clazz.getDeclaredFields();
10 for (Field field : fields) {
11 //查找變量中是否有存在@AnonTest註解
12 if (field.isAnnotationPresent(AnonTest.class)) {
13 //若是存在,則作相應處理
14 AnonTest anon= field.getAnnotation(AnonTest.class);
15 System.out.println("名稱是:"+anon.name());
16 System.out.println("年齡是:"+anon.age());
17 System.out.println("上報信息到用戶年齡分佈表中");
18 //上報信息到用戶年齡分佈表中
19 //uploadInfoToLog(anon.name()),anon.age())
20 }
21 }
22 }
23}
複製代碼
輸出:
1名稱是:張小龍
2年齡是:25
3上報信息到用戶年齡分佈表中
複製代碼
最終程序按照咱們的需求運行而且輸出正確的信息,因而可知,註解對於Java來講是一種補充,他的存在與否對程序來講應該是無影響的,靈活使用註解能使開發的效率更高,並且程序規範性也獲得加強。
以爲本文對你有幫助?請分享給更多人
關注「編程無界」,提高裝逼技能