注解使用入門(一)


本篇博客要講解主要分為以下幾個問題

  1. 注解的相關知識點
  2. 基於運行時的注解的例子解析說明

至於關於編譯時的注解,待下篇博客的時候會結合例子講解一下,目前我也正在學習當中

注解的相關知識點

提到注解,大多數人應該都不默認,在我們程序中見到的@Override,@Deprected,@SupressWarnings等等,這些都是注解,只不過是系統自己封裝好的,而我們平時比較少去深入理解是怎樣實現的?

1)什么是注解(Annotation):

Annotation(注解)就是Java提供了一種元程序中的元素關聯任何信息和着任何元數據(metadata)的途徑和方法。Annotion(注解)是一個接口,程序可以通過反射來獲取指定程序元素的Annotion對象,然后通過Annotion對象來獲取注解里面的元數據。

2)注解的分類:

根據注解參數的個數,我們可以將注解分為三類:

  1. 標記注解:一個沒有成員定義的Annotation類型被稱為標記注解。這種Annotation類型僅使用自身的存在與否來為我們提供信息。比如后面的系統注解@Override;
  2. 單值注解
  3. 完整注解 

根據注解使用方法和用途,我們可以將Annotation分為三類:

  1. JDK內置系統注解
  2. 元注解
  3. 自定義注解

3)元注解:

元注解的作用就是負責注解其他注解。Java5.0定義了4個標准的meta-annotation類型,它們被用來提供對其它annotation類型作說明。Java5.0定義的元注解:

  1. @Target,
  2. @Retention,
  3. @Documented,
  4. @Inherited

4)元注解解析說明

  • @Documented 是否會保存到 Javadoc 文檔中

  • @Retention 保留時間,可選值

SOURCE(源碼時),CLASS(編譯時),RUNTIME(運行時),默認為 CLASS,SOURCE 大都為 Mark Annotation,這類 Annotation 大都用來校驗,比如 Override, SuppressWarnings

  • @Target 可以用來修飾哪些程序元素,如 TYPE, METHOD, CONSTRUCTOR, FIELD, PARAMETER 等,未標注則表示可修飾所有

ANONOTATION_TYPE(注解類型聲明),
PACKAGE(包)
TYPE (類,包括enum及接口,注解類型)
METHOD (方法)
CONSTRUCTOR (構造方法)
FIFLD (成員變量)
PARAMATER (參數)
LOCAL_VARIABLE (局部 變量)

  • @Inherited 是否可以被繼承,默認為 false

5)什么是metadata(元數據):

元數據從metadata一詞譯來,就是“關於數據的數據”的意思。
 
 元數據的功能作用有很多,比如:你可能用過Javadoc的注釋自動生成文檔。這就是元數據功能的一種。總的來說,元數據可以用來創建文檔,跟蹤代碼的依賴性,執行編譯時格式檢查,代替已有的配置文件。如果要對於元數據的作用進行分類,目前還沒有明確的定義,不過我們可以根據它所起的作用,大致可分為三類:

  1. 編寫文檔:通過代碼里標識的元數據生成文檔
  2. 代碼分析:通過代碼里標識的元數據對代碼進行分析
  3. 編譯檢查:通過代碼里標識的元數據讓編譯器能實現基本的編譯檢查

其他知識點暫時不介紹,個人覺得一下子介紹太多概念很難消化。下面讓我們一起結合例子來使用它。


下面我們來看一下我們要怎樣寫一個基於編譯時的自定義注解的例子

自定義注解大概可分為以下三個步驟:

  1. 自定義一個注解
  2. 在其他類使用我們的注解
  3. 在運行的時候解析我們的注解

解析運行流程圖

1)首先我們我們來看一下我們是怎樣自定義一個注解的

這些類型和它們所支持的類在java.lang.annotation包中可以找到。

/* 
 * 定義注解 MethodInfo 
 * 為方便測試:注解目標為類 方法,屬性及構造方法 
 * 注解中含有三個元素 id ,name和 gid; 
 * id 元素 有默認值 0
 */ 

@Documented 
@Target({ElementType.TYPE,ElementType.METHOD,
	ElementType.FIELD,ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MethodInfo {
	String name() default "xujunTest";
	int id() default 0;
	Class<Long> gid();
}

解析說明

  • (1). 通過 @interface 定義,注解名即為自定義注解名,這里注解名為MethodInfo

  • (2). 注解配置參數名為注解類的方法名,且:

    a. 所有方法沒有方法體,沒有參數沒有修飾符,實際只允許 public & abstract 修飾符,默認為 public,不允許拋異常

    b. 方法返回值只能是基本類型,String, Class, annotation, enumeration 或者是他們的一維數組

    c. 若只有一個默認屬性,可直接用 value() 函數。一個屬性都沒有表示該 Annotation 為 Mark Annotation

  • (3). 可以加 default 表示默認值,如

String name() default "xujunTest";

2)接着我們來看一下我們要怎樣使用我們自定義的注解

/**
 * 這個類專門用來測試注解使用
 * @author xujun
 */
 //類成員注解
@MethodInfo (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){ 
		
	}
}

3)最后我們一起來看一下我們怎樣在運行的時候解析我們的Annotation注解

運行時 Annotation 解析

(1) 運行時 Annotation 指 @Retention 為 RUNTIME 的 Annotation,可手動調用下面常用 API 解析

method.getAnnotation(AnnotationName.class);
method.getAnnotations();
method.isAnnotationPresent(AnnotationName.class);

其他 @Target 如 Field,Class 方法類似

  • getAnnotation(AnnotationName.class) 表示得到該 Target 某個 Annotation 的信息,因為一個 Target 可以被多個 Annotation 修飾
/*
* 根據注解類型返回方法的指定類型注解
*/
MethodInfo annotation = (MethodInfo) constructor
                     .getAnnotation(MethodInfo.class);
  • getAnnotations() 則表示得到該 Target 所有 Annotation
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
	MethodInfo methodInfo = (MethodInfo) annotation
}
  • isAnnotationPresent(AnnotationName.class) 表示該 Target 是否被某個 Annotation 修飾
/*
* 判斷構造方法中是否有指定注解類型的注解
*/
boolean hasAnnotation = constructor
.isAnnotationPresent(MethodInfo.class);
if (hasAnnotation) {
    /*
    * 根據注解類型返回方法的指定類型注解
    */
    MethodInfo annotation = (MethodInfo) constructor
    .getAnnotation(MethodInfo.class);
}


測試代碼

public class ParseAnnotation {
	
	static String className="com.xujun.animation.test.UserAnnotation";
	/**
	 * 簡單打印出UserAnnotation 類中所使用到的類注解 該方法只打印了 Type 類型的注解
	 * 
	 * @throws ClassNotFoundException
	 */
	public static void parseTypeAnnotation() throws ClassNotFoundException {
		Class clazz = Class.forName(className);

		Annotation[] annotations = clazz.getAnnotations();
		for (Annotation annotation : annotations) {
			MethodInfo testA = (MethodInfo) annotation;
			System.out.println("id= \"" + testA.id() + "\"; name= \""
					+ testA.name() + "\"; gid = " + testA.gid());
		}
	}

	/**
	 * 簡單打印出UserAnnotation 類中所使用到的方法注解 該方法只打印了 Method 類型的注解
	 * 
	 * @throws ClassNotFoundException
	 */
	public static void parseMethodAnnotation() {
		Method[] methods = UserAnnotation.class.getDeclaredMethods();
		for (Method method : methods) {
			/*
			 * 判斷方法中是否有指定注解類型的注解
			 */
			boolean hasAnnotation = method.isAnnotationPresent(MethodInfo.class);
			if (hasAnnotation) {
				/*
				 * 根據注解類型返回方法的指定類型注解
				 */
				MethodInfo annotation = method.getAnnotation(MethodInfo.class);
				System.out.println("method = " + method.getName() + " ; id = "
						+ annotation.id() + " ; description = "
						+ annotation.name() + "; gid= " + annotation.gid());
			}
		}
	}

	/**
	 * 簡單打印出UserAnnotation 類中所使用到的方法注解 該方法只打印了 Method 類型的注解
	 * 
	 * @throws ClassNotFoundException
	 */
	public static void parseConstructAnnotation() {
		Constructor[] constructors = UserAnnotation.class.getConstructors();
		for (Constructor constructor : constructors) {
			/*
			 * 判斷構造方法中是否有指定注解類型的注解
			 */
			boolean hasAnnotation = constructor
					.isAnnotationPresent(MethodInfo.class);
			if (hasAnnotation) {
				/*
				 * 根據注解類型返回方法的指定類型注解
				 */
				MethodInfo annotation = (MethodInfo) constructor
						.getAnnotation(MethodInfo.class);
				System.out.println("constructor = " + constructor.getName()
						+ " ; id = " + annotation.id() + " ; description = "
						+ annotation.name() + "; gid= " + annotation.gid());
			}
		}
	}

	public static void main(String[] args) throws ClassNotFoundException {
		parseTypeAnnotation();
		parseMethodAnnotation();
		parseConstructAnnotation();
	}
}

運行以上測試程序,將可以看到以下輸出結果

id= "0"; name= "type"; gid = class java.lang.Long
method = c ; id = 5 ; description = private method; gid= class java.lang.Long
method = b ; id = 4 ; description = protected method; gid= class java.lang.Long
method = a ; id = 3 ; description = public method; gid= class java.lang.Long
constructor = com.xujun.animationdemo.UserAnnotation ; id = 2 ; description = construct; gid= class java.lang.Long


轉載請注明原博客地址:

源碼下載地址:

相關博客推薦

java Type 詳解

java 反射機制詳解

注解使用入門(一)

Android 自定義編譯時注解1 - 簡單的例子

Android 編譯時注解 —— 語法詳解

帶你讀懂 ButterKnife 的源碼

掃一掃,歡迎關注我的微信公眾號 stormjun94(徐公碼字), 目前是一名程序員,不僅分享 Android開發相關知識,同時還分享技術人成長歷程,包括個人總結,職場經驗,面試經驗等,希望能讓你少走一點彎路。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM