1. 前言
前幾年我們的項目還在structs 2 上跑,有一次問一個同事是否知道
Spring Boot,同事說那不是用注解來開發(fā)的嗎。雖然這個答案并不完全對,但是從客觀上Spring Boot對剛剛接觸它的人來說最醒目的就是注解了。那么今天我們來了解一下Java語言的核心功能——注解。
2.注解是什么
public @interface Anno {
}
以上就是一個最簡單的注解聲明。它可以注釋到類、接口、方法以及變量上。通過向方法,接口,類或字段添加注釋,為其綁定的源代碼分配額外的元數(shù)據(jù)。
3.注解的用途
通過注解我們可以通知編譯器有關(guān)警告和錯誤的信息在編譯時操作源代碼在運行時修改或檢查行為。jdk提供內(nèi)置5個基本注解來處理代碼檢查。
- @Override 來標(biāo)記該方法重寫或替換繼承的方法的行為。如果你重寫了父類方法不帶該注解會觸發(fā)一些警告。
- @SuppressWarnings 表示我們要忽略部分代碼中的某些警告。如忽略潛在的類型不安全轉(zhuǎn)換警告unchecked。
- @Deprecated 用來表示類、方法已經(jīng)過時,不推薦使用。如果你強行使用編譯器會在編譯時進行警告。
- @Safevarargs 抑制“堆污染”警告?!岸盐廴尽敝傅氖菍⒁粋€不帶泛型的對象賦給帶泛型的變量時引發(fā)的的類型問題。如果你不想看到該警告就可以使用該注解來抑制。
- @FunctionalInterface java 8 新增注解,只能作用于接口上來標(biāo)識該接口是函數(shù)式接口。java中函數(shù)式接口表示該接口只能有一個抽象方法。如果一個接口被此注解修飾,如果添加第二個抽象方法將無法通過編譯。
注解可以將一些元數(shù)據(jù)傳遞給你編寫的邏輯。比如Spring Mvc 中的一個常用注解@RequestMapping,我們可以通過value參數(shù)來傳遞一個path路徑,Spring Mvc通過對請求的路徑的匹配來作出是否路由到該path上。 目前大量的的框架都依賴注解,比如Spring、hibernate、dubbo等等。
4.元注解
元注解是可以應(yīng)用于其他注解的注解。來增強或者配置目標(biāo)注解的機制。jdk目前提供了5個元注解。如果你需要開發(fā)自定義注解,請務(wù)必熟悉它們:
-
@Retention 只能用于修飾注解,來指定被修飾注解可以保留多長時間。規(guī)定了三種策略:
- RetentionPolicy.SOURCE 這種策略下被修飾的注解只能存在于源代碼中,編譯后被丟棄,通過反射無法獲取到被修飾的注解。
- RetentionPolicy.CLASS 這種策略下被修飾的注解會被編譯進字節(jié)碼文件中。但是JVM無法獲取到被修飾的注解。這是一個默認(rèn)值,當(dāng)你聲明的注解沒有添加任何保留策略時,會默認(rèn)指定該策略。
- RetentionPolicy.RUNTIME 這種策略下被修飾的注解不但可以編譯進字節(jié)碼文件。而且JVM也可以獲取被該注解修飾的注解。而且程序編碼也可以通過反射來獲取被該注解修飾的注解的一些元信息。
@Target 用于指定被修飾注解的修飾目標(biāo)類型。如果一個注解明確了可修飾的目標(biāo)類型,則只能修飾指定的類型。由枚舉ElementType來規(guī)定。
TYPE 只能修飾 類、接口、枚舉。
FIELD 只能修飾成員變量,包含枚舉內(nèi)的常量。
METHOD 只能修飾方法。
PARAMETER 只能修飾參數(shù)。
CONSTRUCTOR 只能修飾構(gòu)造器。
LOCAL_VARIABLE 只能修飾局部變量。
ANNOTATION_TYPE 只能修飾注解。
PACKAGE 只能修飾包定義。也就是package-info.java中
TYPE_PARAMETER java 8 新增 表示該注解能寫在類型參數(shù)的聲明語句中。 類型參數(shù)聲明如: <t style="box-sizing: border-box;">、</t>
TYPE_USE java 8 新增 注解可以再任何用到類型的地方使用。
@Documented 被該注解修飾的注解可以被javadoc工具提取為文檔。
@Inherited 被該注解修飾的注解有繼承性。這里要注意一些要點首先這種繼承性體現(xiàn)的類之間而不是接口之間,而且注解必須是對JVM可見。也就是@Retention為RetentionPolicy.RUNTIME 才起作用。
@Repeatable java 8 新增。在此之前在同一個元素上同一個注解只能出現(xiàn)一次。@Repeatable可以讓一個注解多次出現(xiàn)在一個元素上。
5.自定義注解
自定義注解跟自定義接口類似,但是還有一些區(qū)別,實際開發(fā)你需要對自定義注解進行元注解注釋。注解中的成員變量以無參抽象方法來聲明,成員變量并不是所有類型都支持,目前只支持以下類型:
所有基本類型(int,float,boolean,byte,double,char,long,short)
String
Class (如:Class<?> 或 Class<t style="box-sizing: border-box;">)</t>
enum java枚舉
-
Annotation
下面我們就來自定義一個注解:
/**
* 聲明一個可以標(biāo)記在類、接口、枚舉、方法上的注解。
* 并且JVM Runtime 可見、可生成文檔
*
* @author Dax
* @since 17 :27 2019/9/4
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Anno {
/**
* 若方法名為value且注解聲明只需要聲明value屬性時,
* value可以省略, @Anno("anno") 等同于 @Anno(value="anno")
*
* @return the string
*/
String value();
/**
* 一個具有默認(rèn)值的 String 類型屬性。
* name若不顯式聲明,則默認(rèn)值為"" 。
* 聲明默認(rèn)值通過 default + 默認(rèn)值 來聲明
*
* @return the string
*/
String name() default "";
/**
* 一個Class 類型屬性,沒有默認(rèn)值。其他支持類型不再舉例
*
* @return the class
*/
Class<?> clazz();
}
如何獲取注解中的元數(shù)據(jù)
所有的注解都是java.lang.annotation.Annotation 的子類。只有RetentionPolicy為RUNTIME的 注解才能通過反射獲取。在反射包中提供了AnnotatedElement 接口來對元素上的可捕捉到的注解進行處理。該接口是Class、Method、Constructor等程序元素對象的父接口。也就是說只要能獲取程序元素對象就能對其存在的注解進行處理。主要方法有:
- boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) 判斷是否存在 annotationClass類型的注解。
- <t extends="" annotation="" style="box-sizing: border-box;">T getAnnotation(Class<t style="box-sizing: border-box;"> annotationClass) 如果在當(dāng)前元素上存在參數(shù)所指定類型(annotationClass)的注解,則返回對應(yīng)的注解,否則將返回null。</t></t>
- Annotation[] getAnnotations() 返回在這個元素上的所有注解。如果該元素沒有注解,則返回值是長度為0的數(shù)組。該方法的調(diào)用者可以自由地修改返回的數(shù)組;它不會對返回給其他調(diào)用者的數(shù)組產(chǎn)生影響。
- <t extends="" annotation="" style="box-sizing: border-box;">T[] getAnnotationsByType(Class<t style="box-sizing: border-box;"> annotationClass) 返回與該元素相關(guān)聯(lián)的注解。如果沒有與此元素相關(guān)聯(lián)的注解,則返回值是長度為0的數(shù)組。這個方法與getAnnotation(Class)的區(qū)別在于,該方法檢測其參數(shù)是否為可重復(fù)的注解類型(JLS 9.6),如果是,則嘗試通過“l(fā)ooking through”容器注解來查找該類型的一個或多個注解。該方法的調(diào)用者可以自由地修改返回的數(shù)組;它不會對返回給其他調(diào)用者的數(shù)組產(chǎn)生影響。參考@Repeatable。</t></t>
- <t extends="" annotation="" style="box-sizing: border-box;">T getDeclaredAnnotation(Class<t style="box-sizing: border-box;"> annotationClass) 如果參數(shù)中所指定類型的注解是直接存在于當(dāng)前元素上的,則返回對應(yīng)的注解,否則將返回null。這個方法忽略了繼承的注解。(如果沒有直接在此元素上顯示注釋,則返回null。)</t></t>
- <t extends="" annotation="" style="box-sizing: border-box;">T[] getDeclaredAnnotationsByType(Class<t style="box-sizing: border-box;"> annotationClass) 可獲取重復(fù)注解但是忽略掉繼承注解。</t></t>
- Annotation[] getDeclaredAnnotations() 跟上面的注解區(qū)別在于不能獲取重復(fù)注解。
基本上對這個接口的方法進行學(xué)習(xí)后就可以知道如何獲取注解的元數(shù)據(jù)了。下面我們寫一個例子,還是上面的Anno注解為例:
/**
* 被注解標(biāo)記的類
**/
@Anno("hello")
public class Foo {}
/**
* 通過獲取Foo 的Class 類,
* 然后就可以根據(jù)上面已經(jīng)介紹的方法來獲取value的值了
* @author dax
* @since 2019/9/4 22:17
*/
public class Main {
public static void main(String[] args) {
Anno annotation = Foo.class.getAnnotation(Anno.class);
String value = annotation.value();
System.out.println("value = " + value);
}
}
總結(jié)
今天我們系統(tǒng)地對注解進行了歸納,相信你已經(jīng)對注解有了系統(tǒng)性的認(rèn)識。其實注解還可以干一些花式操作,比如lombok框架。后面我們會介紹相關(guān)的注解技術(shù),多多關(guān)注。
原創(chuàng)作者:碼農(nóng)小胖哥
轉(zhuǎn)載地址:https://gper.club/articles/7e7e7f7ff7g58gc3g6f