註解(Annotation)java
一、Annotation的工做原理:spring
JDK5.0中提供了註解的功能,容許開發者定義和使用本身的註解類型。該功能由一個定義註解類型的語法和描述一個註解聲明的語法,讀取註解的API,一個使用註解修飾的class文件和一個註解處理工具組成。編程
Annotation並不直接影響代碼的語義,可是他能夠被看作是程序的工具或者類庫。它會反過來對正在運行的程序語義有所影響。數組
Annotation能夠從源文件、class文件或者在運行時經過反射機制多種方式被讀取。框架
有如下幾種:ide
1,生成文檔。這是最多見的,也是java 最先提供的註解。函數
2,跟蹤代碼依賴性,實現替代配置文件功能。測試
比較常見的是spring 2.5 開始的基於註解配置。做用就是減小配置。如今的框架基本都 使用了這種配置來減小配置文件的數量。網站
3,在編譯時進行格式檢查。
如@override 放在方法前,若是你這個方法並非覆蓋了超類方法,則編譯時就能檢查 出。
*@interface用來聲明一個註解,其中的每個方法其實是聲明瞭一個配置參數。
*方法的名稱就是參數的名稱,返回值類型就是參數的類型。
*能夠經過default來聲明參數的默認值。
Java.lang.annotation
包 java.lang.annotation 中包含全部已定義或自定義註解所需用到的原註解和接口。如接口 java.lang.annotation.Annotation 是全部註解繼承的接口,而且是自動繼承,不須要定義時指定,相似於全部類都自動繼承Object。
該包同時定義了四個元註解;
註釋類型 Override
@Target(value=METHOD)
@Retention(value=SOURCE)
public @interface Override
@Override註解表示子類要重寫父類的對應方法。
下面是一個使用@Override註解的例子:
class A {
private String id;
A(String id){
this.id = id;
}
@Override
public String toString() {
return id;
}
}
註釋類型 Deprecated
@Documented
@Retention(value=RUNTIME)
public @interface Deprecated
@Deprecated註解表示方法是不被建議使用的。
下面是一個使用@Deprecated註解的例子:
class A {
private String id;
A(String id){
this.id = id;
}
@Deprecated
public void execute(){
System.out.println(id);
}
public static void main(String[] args) {
A a = new A("a123");
a.execute();
}
}
註釋類型 SuppressWarnings
@Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
@Retention(value=SOURCE)
public @interface SuppressWarnings
指示應該在註釋元素(以及包含在該註釋元素中的全部程序元素)中取消顯示指定的編譯器警告。注意,在給定元素中取消顯示的警告集是全部包含元素中取消顯示的警告的超集。例如,若是註釋一個類來取消顯示某個警告,同時註釋一個方法來取消顯示另外一個警告,那麼將在此方法中同時取消顯示這兩個警告。
下面是一個使用@SuppressWarnings註解的例子:
@SuppressWarnings("unchecked")
public static void main(String[] args) {
List list = new ArrayList();
list.add("abc");
}
使用@interface自定義註解時,自動繼承了java.lang.annotation.Annotation接口,由編譯程序自動完成其餘細節。在定義註解時,不能繼承其餘的註解或接口。
1,自定義最簡單的註解:
public @interface MyAnnotation {
}
使用自定義註解:
public class AnnotationTest2 {
@MyAnnotation
public void execute(){
System.out.println("method");
}
}
2,添加變量:
public @interface MyAnnotation {
String value1();
}
使用自定義註解:
public class AnnotationTest2 {
@MyAnnotation(value1="abc")
public void execute(){
System.out.println("method");
}
}
添加默認值:
public @interface MyAnnotation {
String value1() default "abc";
}
三、多變量使用枚舉:
public @interface MyAnnotation {
String value1() default "abc";
MyEnum value2() default MyEnum.Sunny;
}
enum MyEnum{
Sunny,Rainy
}
使用自定義註解:
public class AnnotationTest2 {
@MyAnnotation(value1="a", value2=MyEnum.Sunny)
public void execute(){
System.out.println("method");
}
}
四、數組變量:
public @interface MyAnnotation {
String[] value1() default "abc";
}
使用自定義註解:
public class AnnotationTest2 {
@MyAnnotation(value1={"a","b"})
public void execute(){
System.out.println("method");
}
}
當註解中使用的屬性名爲value時,對其賦值時能夠不指定屬性的名稱而直接寫上屬性值接口;除了value意外的變量名都須要使用name=value的方式賦值。
五、限定註解的使用:
限定註解使用@Target。
@Documented
@Retention(value=RUNTIME)
@Target(value=ANNOTATION_TYPE)
public @interface Target
指示註釋類型所適用的程序元素的種類。若是註釋類型聲明中不存在 Target 元註釋,則聲明的類型能夠用在任一程序元素上。若是存在這樣的元註釋,則編譯器強制實施指定的使用限制。 例如,此元註釋指示該聲明類型是其自身,即元註釋類型。它只能用在註釋類型聲明上:
@Target(ElementType.ANNOTATION_TYPE)
public @interface MetaAnnotationType {
...
}
此元註釋指示該聲明類型只可做爲複雜註釋類型聲明中的成員類型使用。它不能直接用於註釋:
@Target({})
public @interface MemberType {
...
}
這是一個編譯時錯誤,它代表一個 ElementType 常量在 Target 註釋中出現了不僅一次。例如,如下元註釋是非法的:
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.FIELD})
public @interface Bogus {
...
}
public enum ElementType
extends Enum<ElementType>
程序元素類型。此枚舉類型的常量提供了 Java 程序中聲明的元素的簡單分類。
這些常量與 Target 元註釋類型一塊兒使用,以指定在什麼狀況下使用註釋類型是合法的。
ANNOTATION_TYPE
註釋類型聲明
CONSTRUCTOR
構造方法聲明
FIELD
字段聲明(包括枚舉常量)
LOCAL_VARIABLE
局部變量聲明
METHOD
方法聲明
PACKAGE
包聲明
PARAMETER
參數聲明
TYPE
類、接口(包括註釋類型)或枚舉聲明
註解的使用限定的例子:
@Target(ElementType.METHOD)
public @interface MyAnnotation {
String[] value1() default "abc";
}
元註解是指註解的註解。包括 @Retention @Target @Document @Inherited四種。
@Target 表示該註解用於什麼地方,可能的值在枚舉類 ElemenetType 中,包括: @Target(ElementType.TYPE) //接口、類、枚舉、註解
@Target(ElementType.FIELD) //字段、枚舉的常量
@Target(ElementType.METHOD) //方法
@Target(ElementType.PARAMETER) //方法參數
@Target(ElementType.CONSTRUCTOR) //構造函數
@Target(ElementType.LOCAL_VARIABLE)//局部變量
@Target(ElementType.ANNOTATION_TYPE)//註解
@Target(ElementType.PACKAGE) //包 packag註解必須在package-info.java 中聲明
@Retention 表示在什麼級別保存該註解信息。可選的參數值在枚舉類型 RetentionPolicy 中,包括:
@Retention(RetentionPolicy.SOURCE)
//註解僅存在於源碼中,在class字節碼文件中不包含
@Retention(RetentionPolicy.CLASS)
// 默認的保留策略,註解會在class字節碼文件中存在,但運行時沒法得到,
@Retention(RetentionPolicy.RUNTIME)
// 註解會在class字節碼文件中存在,在運行時能夠經過反射獲取到
@Documented 將此註解包含在 javadoc 中 ,它表明着此註解會被javadoc工具提取成文 檔。在doc文檔中的內容會由於此註解的信息內容不一樣而不一樣。至關與 @see,@param 等。 @Inherited 容許子類繼承父類中的註解
@Target用來聲明註解能夠被添加在哪些類型的元素上,如類型、方法和域等。
例:
@Target({TYPE,METHOD,FIELD,CONSTRUCTOR})
public @interface TestA {
//這裏定義了一個空的註解。
}
這個類專門用來測試註解使用
@TestA //使用了類註解
public class UserAnnotation {
@TestA //使用了類成員註解
private Integer age;
@TestA //使用了構造方法註解
public UserAnnotation(){
}
@TestA //使用了類方法註解
public void a(){
@TestA //使用了局部變量註解
Map m = new HashMap(0);
}
@TestA //使用了方法參數註解
public void b(@TestA Integer a){
}
}
@Retention用來聲明註解的保留策略,有CLASS、RUNTIME和SOURCE這三種,
分別表示註解保存在類文件、JVM運行時刻和源代碼中。
只有當聲明爲RUNTIME的時候,纔可以在運行時刻經過反射API來獲取到註解的信息。
無屬性註解
@Retention 參數 RetentionPolicy。這個註解尚未特殊的屬性值。 簡單演示下如何使用:
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
/*
* 定義註解 Test
* 首先使用ElementType.TYPE(須要在package-info.java中聲明)
* 運行級別定爲 運行時,以便後面測試解析
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestA {}
有屬性註解
@interface用來聲明一個註解,其中的每個方法其實是聲明瞭一個配置參數。方法的名稱就是參數的名稱,返回值類型就是參數的類型(返回值類型只能是基本類型、Class、String、enum)。能夠經過default來聲明參數的默認值。
代碼:
/*
* 定義註解 Test
* 爲方便測試:註解目標爲類 方法,屬性及構造方法
* 註解中含有三個元素 id ,name和 gid;
* id 元素 有默認值 0
*/
@Target({TYPE,METHOD,FIELD,CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
public @interface TestA {
String name();
int id() default 0;
Class<Long> gid();}
測試類
import java.util.HashMap;
import java.util.Map;
/**
* 這個類專門用來測試註解使用
*/
@TestA(name="type",gid=Long.class) //類成員註解
public class UserAnnotation {
@TestA(name="param",id=1,gid=Long.class) //類成員註解
private Integer age;
@TestA (name="construct",id=2,gid=Long.class)//構造方法註解
public UserAnnotation(){}
@TestA(name="public method",id=3,gid=Long.class) //類方法註解
public void a(){
Map<String,String> m = new HashMap<String,String>(0);
}
@TestA(name="protected method",id=4,gid=Long.class) //類方法註解
protected void b(){
Map<String,String> m = new HashMap<String,String>(0);
}
@TestA(name="private method",id=5,gid=Long.class) //類方法註解
private void c(){
Map<String,String> m = new HashMap<String,String>(0);
}
public void b(Integer a){ }
}
讀取定義註解內容
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class ParseAnnotation {
/**
*A 簡單打印出UserAnnotation 類中所使用到的類註解
* 該方法只打印了 Type 類型的註解
* @throws ClassNotFoundException
*/
public static void parseTypeAnnotation() throws ClassNotFoundException {
Class clazz = Class.forName("com.tmser.annotation.UserAnnotation");
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
TestA testA = (TestA)annotation;
System.out.println("id= \""+testA.id()+"\"; name= \""+testA.name()+"\"; gid = "+testA.gid());
}
}
//結果爲id="0";name="type";gid =classjava.lang.Long
/*
* B簡單打印出UserAnnotation 類中所使用到的方法註解
* 該方法只打印了 Method 類型的註解
* @throws ClassNotFoundException
*/
public static void parseMethodAnnotation(){
Method[] methods = UserAnnotation.class.getDeclaredMethods();
for (Method method : methods) {
// 判斷方法中是否有指定註解類型的註解
boolean hasAnnotation = method.isAnnotationPresent(TestA.class);
if (hasAnnotation) {
//根據註解類型返回方法的指定類型註解
TestA annotation = method.getAnnotation(TestA.class);
System.out.println("method = " + method.getName()
+ " ; id = " + annotation.id() + " ; description = "
+ annotation.name() + "; gid= "+annotation.gid());
}
}
}
method = c ; id = 5 ; description = private method; gid= class java.lang.Long
method = a ; id = 3 ; description = public method; gid= class java.lang.Long
method = b ; id = 4 ; description = protected method; gid= class java.lang.Long
/**
* C簡單打印出UserAnnotation 類中所使用到的構造方法註解
* 該方法只打印了 Method 類型的註解
* @throws ClassNotFoundException
*/
public static void parseConstructAnnotation(){
Constructor[] constructors = UserAnnotation.class.getConstructors();
for (Constructor constructor : constructors) {
//判斷構造方法中是否有指定註解類型的註解
boolean hasAnnotation = constructor.isAnnotationPresent(TestA.class);
if (hasAnnotation) {
//根據註解類型返回方法的指定類型註解
TestA annotation =(TestA) constructor.getAnnotation(TestA.class);
System.out.println("constructor = " + constructor.getName()
+ " ; id = " + annotation.id() + " ; description = "
+ annotation.name() + "; gid= "+annotation.gid());
}
}
}
constructor=com.tmser.annotation.UserAnnotation;id=2;description =construct;gid=classjava.lang.Long
public static void main(String[] args) throws ClassNotFoundException {
parseTypeAnnotation();
parseMethodAnnotation();
parseConstructAnnotation();// field是同樣的,省略之
}
}
在幫助文檔中加入註解:
要想在製做JavaDoc文件的同時將註解信息加入到API文件中,可使用java.lang.annotation.Documented。
在自定義註解中聲明構建註解文檔:
@Documented
public @interface MyAnnotation {
String[] value1() default "abc";
}
使用自定義註解:
public class AnnotationTest2 {
@MyAnnotation(value1={"a","b"})
public void execute(){
System.out.println("method");
}
}
在註解中使用繼承:
默認狀況下註解並不會被繼承到子類中,能夠在自定義註解時加上java.lang.annotation.Inherited註解聲明使用繼承。
@Documented
@Retention(value=RUNTIME)
@Target(value=ANNOTATION_TYPE)
public @interface Inherited
指示註釋類型被自動繼承。若是在註釋類型聲明中存在 Inherited 元註釋,而且用戶在某一類聲明中查詢該註釋類型,同時該類聲明中沒有此類型的註釋,則將在該類的超類中自動查詢該註釋類型。此過程會重複進行,直到找到此類型的註釋或到達了該類層次結構的頂層 (Object) 爲止。若是沒有超類具備該類型的註釋,則查詢將指示當前類沒有這樣的註釋。
注意,若是使用註釋類型註釋類之外的任何事物,此元註釋類型都是無效的。還要注意,此元註釋僅促成從超類繼承註釋;對已實現接口的註釋無效。
下面是使用反射讀取RUNTIME保留策略的Annotation信息的例子:
@Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String[] value1() default "abc"; }
|
使用自定義註解: public class AnnotationTest2 { @MyAnnotation(value1={"a","b"}) @Deprecated public void execute(){ System.out.println("method"); } }
|
讀取註解中的信息: public static void main(String[] args) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { AnnotationTest2 annotationTest2 = new AnnotationTest2(); //獲取AnnotationTest2的Class實例 Class<AnnotationTest2> c = AnnotationTest2.class; //獲取須要處理的方法Method實例 Method method = c.getMethod("execute", new Class[]{}); //判斷該方法是否包含MyAnnotation註解 if(method.isAnnotationPresent(MyAnnotation.class)){ //獲取該方法的MyAnnotation註解實例 MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class); //執行該方法 method.invoke(annotationTest2, new Object[]{}); //獲取myAnnotation String[] value1 = myAnnotation.value1(); System.out.println(value1[0]); } //獲取方法上的全部註解 Annotation[] annotations = method.getAnnotations(); for(Annotation annotation : annotations){ System.out.println(annotation); } }
|
@Documented @Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface Yts { public enum YtsType{util,entity,service,model}; public YtsType classType() default YtsType.util; }
|
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Inherited public @interface HelloWorld { public String name()default ""; }
|
public class ParseAnnotation { public void parseMethod(Class clazz) throws IllegalArgumentException, IllegalAccessException,InvocationTargetException,SecurityException, NoSuchMethodException, InstantiationException{ Object obj = clazz.getConstructor(new Class[]{}).newInstance(new Object[]{}); for(Method method : clazz.getDeclaredMethods()){ HelloWorld say = method.getAnnotation(HelloWorld.class); String name = ""; if(say != null){ name = say.name(); method.invoke(obj, name); } Yts yts = (Yts)method.getAnnotation(Yts.class); if(yts != null){ if(YtsType.util.equals(yts.classType())){ System.out.println("this is a util method"); }else{ System.out.println("this is a other method"); } } } }
@SuppressWarnings("unchecked") public void parseType(Class clazz) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException{ Yts yts = (Yts) clazz.getAnnotation(Yts.class); if(yts != null){ if(YtsType.util.equals(yts.classType())){ System.out.println("this is a util class"); }else{ System.out.println("this is a other class"); } } }
} |
@Yts(classType =YtsType.util) public class SayHell {
@HelloWorld(name = " 小明 ") @Yts public void sayHello(String name){ if(name == null || name.equals("")){ System.out.println("hello world!"); }else{ System.out.println(name + "say hello world!"); } } }
|
public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException, InstantiationException { ParseAnnotation parse = new ParseAnnotation(); parse.parseMethod(SayHell.class); parse.parseType(SayHell.class); } |
1. 要用好註解,必須熟悉java 的反射機制,從上面的例子能夠看出,註解的解析徹底依賴於反射。
2. 不要濫用註解。日常咱們編程過程不多接觸和使用註解,只有作設計,且不想讓設計有過多的配置時。
瞭解更多詳情請登陸超人學院網站http://www.crxy.cn或者每週日晚八點半相約免費公開課 https://ke.qq.com/course/53102#term_id=100145289 具體詳情請聯繫QQ2435014406