【Java.Core】註解 - Annotation

王曉斌發表於2014-04-16

註解(Annotation)為我們在程式碼中新增資訊提供了一種形式化的方法,我們可以在稍後某個時刻方便地使用這些資料(通過解析註解來使用這些資料)。

註解是那些插入到原始碼中用於工具處理的標籤。這些標籤可以在原始碼層次上進行處理,或者可以通過編譯器將它們納入到類檔案中。

註解不會改變對編寫的程式的編譯方式。

為了能夠受益於註解,需要選擇一個處理工具,然後向你的處理工具可以理解的程式碼中插入註解,之後運用該處理工具。


註解的作用

註解常見的作用有以下幾種:

  • 生成文件。這是最常見的,也是java最早提供的註解。常用的有@see @param @return等;
  • 跟蹤程式碼依賴性,實現替代配置檔案功能。如Spring中的基於註解配置。作用是減少配置;
  • 在編譯時進行格式檢查,如@Override放在方法前,如果這個方法並不是覆蓋了超類方法,則編譯時就能檢查出;

標準註解

JDK5.0定義了7個註解介面。其中三個是規則介面,可以用它們來註釋你的原始碼中的項。其他四個是元註釋,用於描述註解介面的行為屬性。


正規註解

@Deprecated註解可以被新增到任何不在鼓勵使用的條目上。當使用一個已過時的條目時,編譯器將會發出警告。

@SuppressWarning註釋會告知編譯器阻止特殊型別的警告資訊。

@Override註釋只能應用到方法上,編譯器會阻止具有這種註釋的方法去覆蓋一個來自於超類的方法。


元註解

@Target元註釋

@Target元註釋可以應用於一個註釋,以限制該註釋可以應用到哪些條目上。

下表顯式了所有可能的取值情況,它們屬於列舉型別ElementType。可以指定任意數量的元素型別,用括號括起來。

元素型別 註釋使用場合

ElementType ANNOTATION_TYPE

註釋型別宣告

ElementType PACKAGE

ElementType TYPE

類(包括enum)及介面(包括註解型別)

ElementType METHOD

方法

ElementType CONSTRUCTOR

構造器

ElementType FIELD

成員域(包括enum常量)

ElementType PARAMETER

方法或構造器引數

ElementType LOCAL_VARIABLE

本地變數

一條沒有@Target限制的註解可以應用與任何條目上。

編譯器只檢查是否將一條註解應用到了某個允許的條目上,否則會導致一個編譯器錯誤。

例如Spring中的註釋@Controller定義:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {

	/**
	 * The value may indicate a suggestion for a logical component name,
	 * to be turned into a Spring bean in case of an autodetected component.
	 * @return the suggested component name, if any
	 */
	String value() default "";

}


@Retention元註解

@Retention元註釋用於指定一條註釋應該保留多長時間,只能將其指定為表中的任意值,預設為RetentionPolicy.CLASS.

保留規則 描述

RetentionPolicy SOURCE

不包括在類檔案*.class中的註釋

RetentionPolicy CLASS

類檔案中的註釋,但是虛擬機器不需要將它們載入

RetentionPolicy RUNTIME

類檔案中的註釋,並有虛擬機器載入,通過反射API可以獲得它們


@Documentd元註解

@Documentd元註釋為像Javadoc這樣的文件工具提供了一些提示。

@Inherited元註解

@Inherited元註解只能應用與對類的註解。如果一個類具有繼承註解,那麼它的所有子類都自動具有同樣的註解。


註解語法

一個註解是由一個註解介面@interface來定義的:

modifier @interface AnnotationName{
	element declaration 1
	element declaration 2
	... 
	...
}

註解中的每一個方法實際上是宣告瞭一個配置引數。方法的名稱就是引數的名稱,返回值型別就是引數的型別,返回值型別只能是基本型別,Class,String,enum,註解型別,或由前面型別所組成的陣列。可以通過default來宣告引數的預設值。


每個元素具有下面這種形式:

type elementName();

或者

type elementName() default value;

例如:下面這個註解具有兩個元素:assingedTo和severity。

public @interface BugReport{
	String assignedTo() default "[none]";
	int severity();
}

每個註解使用時都具有下面這種格式:

@AnnotationName(elementName1 = value1, elementName2 = value2, ... ...)

例如:

@BugReport(assignedTo = "Harry", severity=10)

元素的順序無關緊要,下面這個註解和前面那個一樣:

@BugReport(severity=10, assignedTo = "Harry")

如果某個元素的值並未指定,那麼就使用宣告的預設值。

預設值並不是和註解儲存在一起的,它們是動態計算而來的。例如,如果將元素assignedTo的預設值更改為“[]”,然後重新編譯BugReport介面,那麼註解@BugReport(severity=10)將使用這個新的預設值。


簡化註解的使用:

  • 標記註解:

如果沒有指定元素,要麼是因為註解中沒有任何元素,要麼是因為所有元素都使用預設值,那麼就不需要使用圓括號,例如:

<span style="font-size:10px;">@BugReport</span>

  • 單值註解:

如果一個元素具有特殊的名字value,並且沒有指定其他元素,那麼就可以忽略掉這個元素名及等號這個符號,例如:

<span style="font-size:10px;">public @interface BugReport{
	String value();
}</span>

那麼,可以將這個註解寫成如下形式:

<span style="font-size:10px;">@BugReport("Harry")</span>


所有的註解介面隱式地擴充套件自java.lang.annotation.Annotation介面,這個介面是一個正規介面,不是一個註解介面。

無法擴充套件註解介面,所有的註解介面都直接擴充套件自java.lang.annotation.Annotation。


一個專案可以具有多個註解,只要它們屬於不同的型別即可。當註解一個特定項的時候,不能多次使用同一個註解型別,例如:

<span style="font-size:10px;">@BugReport(severity=11)
@BugReport(severity=12)
void myMethod(){... ...}</span>

就是一種編譯期錯誤。


讀取/使用類中定義的註解

示例:

<span style="font-size:10px;">package annotation;

import java.lang.*;
import java.util.*;

import java.lang.annotation.*;

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
public @interface TestA {
    String name();
    int id() default 0;
    Class<Long> gid();
}
</span>

<span style="font-size:10px;">package annotation;

import java.util.*;
import java.lang.*;

@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(){
    	
    }
    
    @TestA(name="protected method", id = 4, gid= Long.class)
    protected void b(){
    	
    }
    
    @TestA(name="private method", id = 5, gid = Long.class)
    private void c(){
    	
    }
}
</span>

<span style="font-size:10px;">package annotation;

import java.lang.*;
import java.util.*;
import java.lang.annotation.*;
import java.lang.reflect.*;


public class ParseAnnotation {
    public static void parseTypeAnnotation() throws ClassNotFoundException{
    	Class clazz = Class.forName("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());
    	}
    }
    
    public static void parseMethodAnnotation(){
    	Method[] methods = UserAnnotation.class.getDeclaredMethods();
    	for (Method method : methods){
    		boolean hasAnnotation = method.isAnnotationPresent(TestA.class);
    		if (hasAnnotation){
    			TestA testA = (TestA)method.getAnnotation(TestA.class);
    			System.out.println("method = " + method.getName() + " id = " + testA.id() + " name = " + testA.name() + " gid = " + testA.gid());
    		}
    	}
    }
    
    public static void parseConstructAnnotation(){
    	Constructor[] constructors = UserAnnotation.class.getConstructors();
    	for (Constructor constructor : constructors){
    		boolean hasAnnotation = constructor.isAnnotationPresent(TestA.class);
    		if (hasAnnotation){
    			TestA testA = (TestA)constructor.getAnnotation(TestA.class);
    			System.out.println("constructor = " + constructor.getName() + " id = " + testA.id() + " name = " + testA.name() + " gid = " + testA.gid());
    		}
    	}
    }
    
    public static void main(String[] args) throws ClassNotFoundException{
    	parseTypeAnnotation();
    	parseMethodAnnotation();
    	parseConstructAnnotation();
    }
}
</span>

執行結果:

<span style="font-size:10px;">id = 0 name = type gid = class java.lang.Long
method = c id = 5 name = private method gid = class java.lang.Long
method = b id = 4 name = protected method gid = class java.lang.Long
method = a id = 3 name = public method gid = class java.lang.Long
constructor = annotation.UserAnnotation id = 2 name = construct gid = class java.lang.Long</span>







相關文章