當前位置:首頁 » 編程語言 » 註解java

註解java

發布時間: 2022-01-14 14:36:26

1. 深入淺出java註解 / Java註解是如何工作的

給java註解下個定義
用一個詞就可以描述註解,那就是元數據,即一種描述數據的數據。所以,可以說註解就是源代碼的元數據。Annotation是一種應用於類、方法、參數、變數、構造器及包聲明中的特殊修飾符。它是一種由JSR-175標准選擇用來描述元數據的一種工具。
註解的本質
Annotations僅僅是元數據,和業務邏輯無關。理解起來有點困難,但就是這樣。如果Annotations不包含業務邏輯,那麼必須有人來實現這些邏輯。元數據的"使用者"來做這個事情。Annotations僅僅提供它定義的屬性(類/方法/包/域)的信息。Annotations的"使用者"(同樣是一些代碼)來讀取這些信息並實現必要的邏輯。當我們使用Java的標注Annotations(例如@Override)時,JVM就是一個「使用者」,它在位元組碼層面工作。mok:"當我們使用用於Servlet的註解(例如@WebInitParam)時,容器就是一個'使用者';當我們使用框架提供的註解時,框架中的某個組件會負責做讀取後的邏輯處理。"

2. java開發中常用的註解有哪些

Java 註解全面解析,學習java做一個java工程師不但待遇高,而且前途無可限量。為什麼這樣說呢?因為java程序語言作為最流行的計算機開發語言之一,幾乎所有的系統、軟體、app、網頁等都是需要用到java的。
1.基本語法
註解定義看起來很像介面的定義。事實上,與其他任何介面一樣,註解也將會編譯成class文件。
@Target(ElementType.Method)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {}
除了@符號以外,@Test的定義很像一個空的介面。定義註解時,需要一些元註解(meta-annotation),如@Target和@Retention
@Target用來定義註解將應用於什麼地方(如一個方法或者一個域)
@Retention用來定義註解在哪一個級別可用,在源代碼中(source),類文件中(class)或者運行時(runtime)
在註解中,一般都會包含一些元素以表示某些值。當分析處理註解時,程序可以利用這些值。沒有元素的註解稱為標記註解(marker annotation)
四種元註解,元註解專職負責註解其他的註解,所以這四種註解的Target值都是ElementType.ANNOTATION_TYPE
註解 說明
@Target 表示該註解可以用在什麼地方,由ElementType枚舉定義
CONSTRUCTOR:構造器的聲明
FIELD:域聲明(包括enum實例)
LOCAL_VARIABLE:局部變數聲明
METHOD:方法聲明
PACKAGE:包聲明
PARAMETER:參數聲明
TYPE:類、介面(包括註解類型)或enum聲明
ANNOTATION_TYPE:註解聲明(應用於另一個註解上)
TYPE_PARAMETER:類型參數聲明(1.8新加入)
TYPE_USE:類型使用聲明(1.8新加入)
PS:當註解未指定Target值時,此註解可以使用任何元素之上,就是上面的類型
@Retention 表示需要在什麼級別保存該註解信息,由RetentionPolicy枚舉定義
SOURCE:註解將被編譯器丟棄(該類型的註解信息只會保留在源碼里,源碼經過編譯後,註解信息會被丟棄,不會保留在編譯好的class文件里)
CLASS:註解在class文件中可用,但會被VM丟棄(該類型的註解信息會保留在源碼里和class文件里,在執行的時候,不會載入到虛擬機(JVM)中)
RUNTIME:VM將在運行期也保留註解信息,因此可以通過反射機制讀取註解的信息(源碼、class文件和執行的時候都有註解的信息)
PS:當註解未定義Retention值時,默認值是CLASS
@Documented 表示註解會被包含在javaapi文檔中
@Inherited 允許子類繼承父類的註解
2. 註解元素
– 註解元素可用的類型如下:
– 所有基本類型(int,float,boolean,byte,double,char,long,short)
– String
– Class
– enum
– Annotation
– 以上類型的數組
如果使用了其他類型,那編譯器就會報錯。也不允許使用任何包裝類型。註解也可以作為元素的類型,也就是註解可以嵌套。
元素的修飾符,只能用public或default。
– 默認值限制
編譯器對元素的默認值有些過分挑剔。首先,元素不能有不確定的值。也就是說,元素必須要麼具有默認值,要麼在使用註解時提供元素的值。
其次,對於非基本類型的元素,無論是在源代碼中聲明,還是在註解介面中定義默認值,都不能以null作為值。這就是限制,這就造成處理器很難表現一個元素的存在或缺失狀態,因為每個註解的聲明中,所有的元素都存在,並且都具有相應的值。為了繞開這個限制,只能定義一些特殊的值,例如空字元串或負數,表示某個元素不存在。
@Target(ElementType.Method)
@Retention(RetentionPolicy.RUNTIME)
public @interface MockNull {
public int id() default -1;
public String description() default 「」;
}
3. 快捷方式
何為快捷方式呢?先來看下springMVC中的Controller註解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
String value() default 「」;
}
可以看見Target應用於類、介面、註解和枚舉上,Retention策略為RUNTIME運行時期,有一個String類型的value元素。平常使用的時候基本都是這樣的:
@Controller(「/your/path」)
public class MockController { }
這就是快捷方式,省略了名-值對的這種語法。下面給出詳細解釋:
註解中定義了名為value的元素,並且在應用該註解的時候,如果該元素是唯一需要賦值的一個元素,那麼此時無需使用名-值對的這種語法,而只需在括弧內給出value元素所需的值即可。這可以應用於任何合法類型的元素,當然了,這限制了元素名必須為value。
4. JDK1.8註解增強
TYPE_PARAMETER和TYPE_USE
在JDK1.8中ElementType多了兩個枚舉成員,TYPE_PARAMETER和TYPE_USE,他們都是用來限定哪個類型可以進行註解。舉例來說,如果想要對泛型的類型參數進行註解:
public class AnnotationTypeParameter<@TestTypeParam T> {}
那麼,在定義@TestTypeParam時,必須在@Target設置ElementType.TYPE_PARAMETER,表示這個註解可以用來標注類型參數。例如:
@Target(ElementType.TYPE_PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestTypeParam {}
ElementType.TYPE_USE用於標注各種類型,因此上面的例子也可以將TYPE_PARAMETER改為TYPE_USE,一個註解被設置為TYPE_USE,只要是類型名稱,都可以進行註解。例如有如下註解定義:
@Target(ElementType.TYPE_USE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {}
那麼以下的使用註解都是可以的:
List<@Test Comparable> list1 = new ArrayList<>();
List<? extends Comparable> list2 = new ArrayList<@Test Comparable>();
@Test String text;
text = (@Test String)new Object();
java.util. @Test Scanner console;
console = new java.util.@Test Scanner(System.in);
PS:以上@Test註解都是在類型的右邊,要注意區分1.8之前的枚舉成員,例如:
@Test java.lang.String text;
在上面這個例子中,顯然是在進行text變數標注,所以還使用當前的@Target會編譯錯誤,應該加上ElementType.LOCAL_VARIABLE。
@Repeatable註解
@Repeatable註解是JDK1.8新加入的,從名字意思就可以大概猜出他的意思(可重復的)。可以在同一個位置重復相同的註解。舉例:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Filter {
String [] value();
}
如下進行註解使用:
@Filter({「/admin」,」/main」})
public class MainFilter { }
換一種風格:
@Filter(「/admin」)
@Filter(「/main」)
public class MainFilter {}
在JDK1.8還沒出現之前,沒有辦法到達這種「風格」,使用1.8,可以如下定義@Filter:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Filters.class)
public @interface Filter {
String value();
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Filters {
Filter [] value();
}
實際上這是編譯器的優化,使用@Repeatable時告訴編譯器,使用@Filters來作為收集重復註解的容器,而每個@Filter存儲各自指定的字元串值。
JDK1.8在AnnotatedElement介面新增了getDeclaredAnnotationsByType和getAnnotationsByType,在指定@Repeatable的註解時,會尋找重復註解的容器中。相對於,getDeclaredAnnotation和getAnnotation就不會處理@Repeatable註解。舉例如下:
@Filter(「/admin」)
@Filter(「/filter」)
public class FilterClass {
public static void main(String[] args) {
Class<FilterClass> filterClassClass = FilterClass.class;
Filter[] annotationsByType = filterClassClass.getAnnotationsByType(Filter.class);
if (annotationsByType != null) {
for (Filter filter : annotationsByType) {
System.out.println(filter.value());
}
}
System.out.println(filterClassClass.getAnnotation(Filter.class));
}
}
日誌如下:
/admin
/filter
null

望採納!

3. java註解是怎麼實現的

用一個詞就可以描述註解,那就是元數據,即一種描述數據的數據。所以,可以說註解就是源代碼的元數據。比如,下面這段代碼:

@Override
public String toString() {
return "This is String Representation of current object.";
}

上面的代碼中,我重寫了toString()方法並使用了@Override註解。但是,即使我不使用@Override註解標記代碼,程序也能夠正常執行。那麼,該註解表示什麼?這么寫有什麼好處嗎?事實上,@Override告訴編譯器這個方法是一個重寫方法(描述方法的元數據),如果父類中不存在該方法,編譯器便會報錯,提示該方法沒有重寫父類中的方法。如果我不小心拼寫錯誤,例如將toString()寫成了toStrring(){double r},而且我也沒有使用@Override註解,那程序依然能編譯運行。但運行結果會和我期望的大不相同。現在我們了解了什麼是註解,並且使用註解有助於閱讀程序。
Annotation是一種應用於類、方法、參數、變數、構造器及包聲明中的特殊修飾符。它是一種由JSR-175標准選擇用來描述元數據的一種工具。
為什麼要引入註解?
使用Annotation之前(甚至在使用之後),XML被廣泛的應用於描述元數據。不知何時開始一些應用開發人員和架構師發現XML的維護越來越糟糕了。他們希望使用一些和代碼緊耦合的東西,而不是像XML那樣和代碼是松耦合的(在某些情況下甚至是完全分離的)代碼描述。如果你在Google中搜索「XML vs. annotations」,會看到許多關於這個問題的辯論。最有趣的是XML配置其實就是為了分離代碼和配置而引入的。上述兩種觀點可能會讓你很疑惑,兩者觀點似乎構成了一種循環,但各有利弊。下面我們通過一個例子來理解這兩者的區別。
假如你想為應用設置很多的常量或參數,這種情況下,XML是一個很好的選擇,因為它不會同特定的代碼相連。如果你想把某個方法聲明為服務,那麼使用Annotation會更好一些,因為這種情況下需要註解和方法緊密耦合起來,開發人員也必須認識到這點。
另一個很重要的因素是Annotation定義了一種標準的描述元數據的方式。在這之前,開發人員通常使用他們自己的方式定義元數據。例如,使用標記interfaces,注釋,transient關鍵字等等。每個程序員按照自己的方式定義元數據,而不像Annotation這種標準的方式。
目前,許多框架將XML和Annotation兩種方式結合使用,平衡兩者之間的利弊。
Annotation是如何工作的?怎麼編寫自定義的Annotation?
在講述這部分之前,建議你首先下載Annotation的示例代碼AnnotationsSample.zip 。下載之後放在你習慣使用的IDE中,這些代碼會幫助你更好的理解Annotation機制。
編寫Annotation非常簡單,可以將Annotation的定義同介面的定義進行比較。我們來看兩個例子:一個是標準的註解@Override,另一個是用戶自定義註解@Todo。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

對於@Override注釋你可能有些疑問,它什麼都沒做,那它是如何檢查在父類中有一個同名的函數呢。當然,不要驚訝,我是逗你玩的。@Override註解的定義不僅僅只有這么一點代碼。這部分內容很重要,我不得不再次重復:Annotations僅僅是元數據,和業務邏輯無關。理解起來有點困難,但就是這樣。如果Annotations不包含業務邏輯,那麼必須有人來實現這些邏輯。元數據的用戶來做這個事情。Annotations僅僅提供它定義的屬性(類/方法/包/域)的信息。Annotations的用戶(同樣是一些代碼)來讀取這些信息並實現必要的邏輯。
當我們使用Java的標注Annotations(例如@Override)時,JVM就是一個用戶,它在位元組碼層面工作。到這里,應用開發人員還不能控制也不能使用自定義的註解。因此,我們講解一下如何編寫自定義的Annotations。
我們來逐個講述編寫自定義Annotations的要點。上面的例子中,你看到一些註解應用在註解上。
J2SE5.0版本在 java.lang.annotation提供了四種元註解,專門註解其他的註解:
@Documented –註解是否將包含在JavaDoc中
@Retention –什麼時候使用該註解
@Target? –註解用於什麼地方
@Inherited – 是否允許子類繼承該註解
@Documented–一個簡單的Annotations標記註解,表示是否將註解信息添加在java文檔中。
@Retention– 定義該註解的生命周期。
RetentionPolicy.SOURCE – 在編譯階段丟棄。這些註解在編譯結束之後就不再有任何意義,所以它們不會寫入位元組碼。@Override, @SuppressWarnings都屬於這類註解。
RetentionPolicy.CLASS – 在類載入的時候丟棄。在位元組碼文件的處理中有用。註解默認使用這種方式。
RetentionPolicy.RUNTIME– 始終不會丟棄,運行期也保留該註解,因此可以使用反射機制讀取該註解的信息。我們自定義的註解通常使用這種方式。
@Target – 表示該註解用於什麼地方。如果不明確指出,該註解可以放在任何地方。以下是一些可用的參數。需要說明的是:屬性的註解是兼容的,如果你想給7個屬性都添加註解,僅僅排除一個屬性,那麼你需要在定義target包含所有的屬性。
ElementType.TYPE:用於描述類、介面或enum聲明
ElementType.FIELD:用於描述實例變數
ElementType.METHOD
ElementType.PARAMETER
ElementType.CONSTRUCTOR
ElementType.LOCAL_VARIABLE
ElementType.ANNOTATION_TYPE 另一個注釋
ElementType.PACKAGE 用於記錄java文件的package信息
@Inherited – 定義該注釋和子類的關系
那麼,註解的內部到底是如何定義的呢?Annotations只支持基本類型、String及枚舉類型。注釋中所有的屬性被定義成方法,並允許提供默認值。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Todo {
public enum Priority {LOW, MEDIUM, HIGH}
public enum Status {STARTED, NOT_STARTED}
String author() default "Yash";
Priority priority() default Priority.LOW;
Status status() default Status.NOT_STARTED;
}

下面的例子演示了如何使用上面的註解。

@Todo(priority = Todo.Priority.MEDIUM, author = "Yashwant", status = Todo.Status.STARTED)
public void incompleteMethod1() {
//Some business logic is written
//But it』s not complete yet
}

如果註解中只有一個屬性,可以直接命名為「value」,使用時無需再標明屬性名。

@interface Author{
String value();
}
@Author("Yashwant")
public void someMethod() {
}

但目前為止一切看起來都還不錯。我們定義了自己的註解並將其應用在業務邏輯的方法上。現在我們需要寫一個用戶程序調用我們的註解。這里我們需要使用反射機制。如果你熟悉反射代碼,就會知道反射可以提供類名、方法和實例變數對象。所有這些對象都有getAnnotation()這個方法用來返回註解信息。我們需要把這個對象轉換為我們自定義的注釋(使用 instanceOf()檢查之後),同時也可以調用自定義注釋裡面的方法。看看以下的實例代碼,使用了上面的註解:

Class businessLogicClass = BusinessLogic.class;
for(Method method : businessLogicClass.getMethods()) {
Todo todoAnnotation = (Todo)method.getAnnotation(Todo.class);
if(todoAnnotation != null) {
System.out.println(" Method Name : " + method.getName());
System.out.println(" Author : " + todoAnnotation.author());
System.out.println(" Priority : " + todoAnnotation.priority());
System.out.println(" Status : " + todoAnnotation.status());
}
}

4. 註解的JAVA中的註解

java.lang.annotation.Retention可以在您定義Annotation型態時,指示編譯器如何對待您的自定義 Annotation,預設上編譯器會將Annotation資訊留在class檔案中,但不被虛擬機器讀取,而僅用於編譯器或工具程式運行時提供資訊。
在使用Retention型態時,需要提供java.lang.annotation.RetentionPolicy的列舉型態:
package java.lang.annotation;
public enum RetentionPolicy {
SOURCE, //編譯器處理完Annotation資訊後就沒事了
CLASS, //編譯器將Annotation儲存於class檔中,預設
RUNTIME //編譯器將Annotation儲存於class檔中,可由VM讀入
}
RetentionPolicy為SOURCE的例子是SuppressWarnings,這個資訊的作用僅在告知編譯器抑制警訊,所以不必將這個資訊儲存於class檔案。
RetentionPolicy為RUNTIME的時機,可像是您使用Java設計一個程式碼分析工具,您要VM讀出Annotation資訊,以在分析程式中使用,搭配Reflection機制,就可以達到這個目的。
在J2SE 5.0中新增了java.lang.reflect.AnnotatedElement這個界面,當中定義有四個方法:
public Annotation getAnnotation(Class annotationType);
public Annotation[] getAnnotations();
public Annotation[] getDeclaredAnnotations();
public boolean isAnnotationPresent(Class annotationType);
Class、Constructor、Field、Method、Package等類別,都實作了AnnotatedElement這個介面,所以您可以從這些類別的實例上,分別取得標示於其上的Annotation與其資訊,如果RetentionPolicy為RUNTIME的話。
舉個例子來說,假設您設計了以下的Debug Annotation:
* Debug.java
package onlyfun.caterpillar;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Debug {
String value();
String name();
}
由於RetentionPolicy為RUNTIME,編譯器在處理Debug Annotation時,會將之編譯至class檔中,並可以VM讀出Annotation資訊,接著我們將Debug用於程式中:
* SomeObject.java
package onlyfun.caterpillar;
public class SomeObject {
@Debug(
value = unit,
name = debug1
)
public void doSomething() {
// ....
}
}
可以設計一個工具程式來讀取Annotation資訊:
* DebugTool.java
package onlyfun.caterpillar;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
public class DebugTool {
public static void main(String[] args)
throws NoSuchMethodException {
Class<SomeObject> c = SomeObject.class;
Method method = c.getMethod(doSomething);
if(method.isAnnotationPresent(Debug.class)) {
System.out.println(@Debug is found.);
Debug debug = method.getAnnotation(Debug.class);
System.out.println( value = + debug.value());
System.out.println( name = + ());
}
else {
System.out.println(@Debug is not found.);
}
Annotation[] annotations = method.getAnnotations();
for(Annotation annotation : annotations) {
System.out.println(
annotation.annotationType().getName());
}
}
}
程式的執行結果如下:
@Debug is found.
value = unit
name = debug1
onlyfun.caterpillar.Debug

5. Java註解的出現 Java的註解是從何開始的

我不知道知道誰發明了註解,但是可以告訴你,java不同版本間的功能定義是不同的。這個功能的定義不是個人決定的,而是一個叫做JCP的組織, 這個組織雲集了眾多的資深專家,包括頂級開發團隊中的精英。從java5開始出現了註解這個概念。定義註解的標準是JSR-250。註解從一定程度上分擔了xml配置的一些任務(配套的標准如:JPA)。甚至可以在項目中用純註解來配置。幾乎所有的主流框架(除了struts1)都有自己的一套註解。追溯註解的源頭,我個人認為,註解的前身就是我們看到的注釋文檔。標準的注釋文檔中有包括@author等的標注。在註解之前我們可以用Xdoclet來進行項目的配置。可惜這一方法幾乎沒有得到應用。但是註解的出現改變了這一現狀。註解易於定義,包括本身就是java,提供了很好的編譯器支持。我們可以用註解配置對象。這個是xml文件無法做到的。因為註解的易配置性和強靈活性,還有對代碼的執行並不產生影響。註解得到了廣泛的應用。現在,新的項目開發都開始傾向於註解這種新的開發方式,在開發的效率和糾錯性上面。他已經遠遠優於xml配置。加上主流框架的支持及其它的易於實現性。相信他會走的更好。

6. java中注釋和註解於什麼區別啊

首先來說注釋有三種:// /* */ /** */ 前兩種編譯器直接跳過,從來不閱讀,第三種編譯器是可以看懂的,當你使用javadoc這樣的命令時會用到,用來生成API時用的。

註解:這東東完全就是給編譯器看的。 比如@Ovrride表示這個方法是重寫了父類中的方法,而不是自定義的,所以這個時候編譯器會去檢查你的方法名是否和父類一樣,是否寫錯了。

起初,註解是比較簡單的,後來註解裡面可以加入變數和參數,以節省代碼(這些代碼都是大家共同認可的,用一個公式給代替了)

註解是對計算機的說明,也可以不用註解,自己寫代碼告訴計算機編譯器。 註解其實就是代碼,只是看起來和我們自己寫的有點不一樣而已。也是代碼的一部分,學習Hibernate和Spring等的時候會大量用到註解,用來節省大量代碼。

7. java註解是怎麼實現的

//每個註解就是一個類
@註解介面類(介面屬性=值)
//創建一個自定義註解
@Retention(RetentionPolicy.RUNTIME)//運行時獲得
@Target(ElementType.METHOD)//針對方法的註解
public@interface自注標{
int屬性1();
}
//註解加在自己的普通類上
classA{
@自注標(屬性1=123)
publicvoidhello(){
}
}
//在自己框架操作下游開發者的代碼時,通過反射得到該註解的值123
自注標a=A.getClass().getMethod("hello").getAnnotation(自注標.class);
inti=a.屬性1();//就能獲得用戶註解值。進行相應的動作

8. java注釋中的@什麼意思

@XXX代表是註解(Annotation的標准翻譯)。和注釋類似,區別是注釋是給人看的,而註解是給Java系統看的。你可以定義自己的註解,具體內容就很多了,看看書吧,你會有收獲的

9. java註解的類型可以是哪些

使用註解

在一般的Java開發中,最常接觸到的可能就是@Override和@SupressWarnings這兩個註解了。使用@Override的時候只需要一個簡單的聲明即可。這種稱為標記註解(marker annotation ),它的出現就代表了某種配置語義。而其它的註解是可以有自己的配置參數的。配置參數以名值對的方式出現。使用 @SupressWarnings的時候需要類似@SupressWarnings({"uncheck", "unused"})這樣的語法。在括弧裡面的是該註解可供配置的值。由於這個註解只有一個配置參數,該參數的名稱默認為value,並且可以省略。而花括弧則表示是數組類型。在JPA中的@Table註解使用類似@Table(name = "Customer", schema = "APP")這樣的語法。從這里可以看到名值對的用法。在使用註解時候的配置參數的值必須是編譯時刻的常量。

從某種角度來說,可以把註解看成是一個XML元素,該元素可以有不同的預定義的屬性。而屬性的值是可以在聲明該元素的時候自行指定的。在代碼中使用註解,就相當於把一部分元數據從XML文件移到了代碼本身之中,在一個地方管理和維護。


開發註解

在一般的開發中,只需要通過閱讀相關的API文檔來了解每個註解的配置參數的含義,並在代碼中正確使用即可。在有些情況下,可能會需要開發自己的註解。這在庫的開發中比較常見。註解的定義有點類似介面。下面的代碼給出了一個簡單的描述代碼分工安排的註解。通過該註解可以在源代碼中記錄每個類或介面的分工和進度情況。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public@interfaceAssignment{
Stringassignee();
inteffort();
doublefinished()default0;
}

@interface用來聲明一個註解,其中的每一個方法實際上是聲明了一個配置參數。方法的名稱就是參數的名稱,返回值類型就是參數的類型。可以通過default來聲明參數的默認值。在這里可以看到@Retention和@Target這樣的元註解,用來聲明註解本身的行為。@Retention用來聲明註解的保留策略,有CLASS、RUNTIME和SOURCE這三種,分別表示註解保存在類文件、JVM運行時刻和源代碼中。只有當聲明為RUNTIME的時候,才能夠在運行時刻通過反射API來獲取到註解的信息。@Target用來聲明註解可以被添加在哪些類型的元素上,如類型、方法和域等。


處理註解

在程序中添加的註解,可以在編譯時刻或是運行時刻來進行處理。在編譯時刻處理的時候,是分成多趟來進行的。如果在某趟處理中產生了新的Java源文件,那麼就需要另外一趟處理來處理新生成的源文件。如此往復,直到沒有新文件被生成為止。在完成處理之後,再對Java代碼進行編譯。JDK 5中提供了apt工具用來對註解進行處理。apt是一個命令行工具,與之配套的還有一套用來描述程序語義結構的Mirror API。Mirror API(com.sun.mirror.*)描述的是程序在編譯時刻的靜態結構。通過Mirror API可以獲取到被註解的Java類型元素的信息,從而提供相應的處理邏輯。具體的處理工作交給apt工具來完成。編寫註解處理器的核心是AnnotationProcessorFactory和AnnotationProcessor兩個介面。後者表示的是註解處理器,而前者則是為某些註解類型創建註解處理器的工廠。

以上面的註解Assignment為例,當每個開發人員都在源代碼中更新進度的話,就可以通過一個註解處理器來生成一個項目整體進度的報告。 首先是註解處理器工廠的實現。

{
(Set<AnnotationTypeDeclaration>atds,?){
if(atds.isEmpty()){
returnAnnotationProcessors.NO_OP;
}
returnnewAssignmentAp(env);//返回註解處理器
}
publicCollection<String>supportedAnnotationTypes(){
returnCollections.unmodifiableList(Arrays.asList("annotation.Assignment"));
}
publicCollection<String>supportedOptions(){
returnCollections.emptySet();
}
}

AnnotationProcessorFactory介面有三個方法:getProcessorFor是根據註解的類型來返回特定的註解處理器;supportedAnnotationTypes是返回該工廠生成的註解處理器所能支持的註解類型;supportedOptions用來表示所支持的附加選項。在運行apt命令行工具的時候,可以通過-A來傳遞額外的參數給註解處理器,如-Averbose=true。當工廠通過 supportedOptions方法聲明了所能識別的附加選項之後,註解處理器就可以在運行時刻通過的getOptions方法獲取到選項的實際值。註解處理器本身的基本實現如下所示。

{
private;
;
publicAssignmentAp(){
this.env=env;
assignmentDeclaration=(AnnotationTypeDeclaration)env.getTypeDeclaration("annotation.Assignment");
}
publicvoidprocess(){
Collection<Declaration>declarations=env.getDeclarationsAnnotatedWith(assignmentDeclaration);
for(Declarationdeclaration:declarations){
processAssignmentAnnotations(declaration);
}
}
(Declarationdeclaration){
Collection<AnnotationMirror>annotations=declaration.getAnnotationMirrors();
for(AnnotationMirrormirror:annotations){
if(mirror.getAnnotationType().getDeclaration().equals(assignmentDeclaration)){
Map<,AnnotationValue>values=mirror.getElementValues();
Stringassignee=(String)getAnnotationValue(values,"assignee");//獲取註解的值
}
}
}
}

註解處理器的處理邏輯都在process方法中完成。通過一個聲明(Declaration)的getAnnotationMirrors方法就可以獲取到該聲明上所添加的註解的實際值。得到這些值之後,處理起來就不難了。

在創建好註解處理器之後,就可以通過apt命令行工具來對源代碼中的註解進行處理。 命令的運行格式是apt -classpath bin -factory annotation.apt.AssignmentApf src/annotation/work/*.java,即通過-factory來指定註解處理器工廠類的名稱。實際上,apt工具在完成處理之後,會自動調用javac來編譯處理完成後的源代碼。

JDK 5中的apt工具的不足之處在於它是Oracle提供的私有實現。在JDK 6中,通過JSR 269把自定義註解處理器這一功能進行了規范化,有了新的javax.annotation.processing這個新的API。對Mirror API也進行了更新,形成了新的javax.lang.model包。註解處理器的使用也進行了簡化,不需要再單獨運行apt這樣的命令行工具,Java編譯器本身就可以完成對註解的處理。對於同樣的功能,如果用JSR 269的做法,只需要一個類就可以了。

@SupportedSourceVersion(SourceVersion.RELEASE_6)
@SupportedAnnotationTypes("annotation.Assignment")
{
;
publicsynchronizedvoidinit(){
super.init(processingEnv);
ElementselementUtils=processingEnv.getElementUtils();
assignmentElement=elementUtils.getTypeElement("annotation.Assignment");
}
publicbooleanprocess(Set<?extendsTypeElement>annotations,RoundEnvironmentroundEnv){
Set<?extendsElement>elements=roundEnv.getElementsAnnotatedWith(assignmentElement);
for(Elementelement:elements){
processAssignment(element);
}
}
privatevoidprocessAssignment(Elementelement){
List<?extendsAnnotationMirror>annotations=element.getAnnotationMirrors();
for(AnnotationMirrormirror:annotations){
if(mirror.getAnnotationType().asElement().equals(assignmentElement)){
Map<?extendsExecutableElement,?extendsAnnotationValue>values=mirror.getElementValues();
Stringassignee=(String)getAnnotationValue(values,"assignee");//獲取註解的值
}
}
}
}

仔細比較上面兩段代碼,可以發現它們的基本結構是類似的。不同之處在於JDK 6中通過元註解@SupportedAnnotationTypes來聲明所支持的註解類型。另外描述程序靜態結構的javax.lang.model包使用了不同的類型名稱。使用的時候也更加簡單,只需要通過javac -processor annotation.pap.AssignmentProcess Demo1.java這樣的方式即可。

上面介紹的這兩種做法都是在編譯時刻進行處理的。而有些時候則需要在運行時刻來完成對註解的處理。這個時候就需要用到Java的反射API。反射API提供了在運行時刻讀取註解信息的支持。不過前提是註解的保留策略聲明的是運行時。Java反射API的AnnotatedElement介面提供了獲取類、方法和域上的註解的實用方法。比如獲取到一個Class類對象之後,通過getAnnotation方法就可以獲取到該類上添加的指定註解類型的註解。


實例分析

下面通過一個具體的實例來分析說明在實踐中如何來使用和處理註解。假定有一個公司的雇員信息系統,從訪問控制的角度出發,對雇員的工資的更新只能由具有特定角色的用戶才能完成。考慮到訪問控制需求的普遍性,可以定義一個註解來讓開發人員方便的在代碼中聲明訪問控制許可權。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public@interfaceRequiredRoles{
String[]value();
}

下一步則是如何對註解進行處理,這里使用的Java的反射API並結合動態代理。下面是動態代理中的InvocationHandler介面的實現。

<T>implementsInvocationHandler{
finalTaccessObj;
publicAccessInvocationHandler(TaccessObj){
this.accessObj=accessObj;
}
publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{
RequiredRolesannotation=method.getAnnotation(RequiredRoles.class);//通過反射API獲取註解
if(annotation!=null){
String[]roles=annotation.value();
Stringrole=AccessControl.getCurrentRole();
if(!Arrays.asList(roles).contains(role)){
(".");
}
}
returnmethod.invoke(accessObj,args);
}
}

在具體使用的時候,首先要通過Proxy.newProxyInstance方法創建一個EmployeeGateway的介面的代理類,使用該代理類來完成實際的操作。

10. Java 什麼是註解及註解原理詳細介紹

1、註解是針對Java編譯器的說明。

可以給Java包、類型(類、介面、枚舉)、構造器、方法、域、參數和局部變數進行註解。Java編譯器可以根據指令來解釋註解和放棄註解,或者將註解放到編譯後的生成的class文件中,運行時可用。

2、註解和註解類型

註解類型是一種特殊的介面類型,註解是註解註解類型的一個實例。

註解類型也有名稱和成員,註解中包含的信息採用鍵值對形式,可以有0個或多個。

3、Java中定義的一些註解:

@Override 告訴編譯器這個方法要覆蓋一個超類方法,防止程序員覆蓋出錯。

@Deprecated 這個標識方法或類(介面等類型)過期,警告用戶不建議使用。

@SafeVarargs JDK7新增,避免可變參數在使用泛型化時候警告」執行時期無法具體確認參數類型「,當然,也可以用@SuppressWarnings來避免檢查,顯然後者的抑制的范圍更大。

@SuppressWarnings(value={"unchecked"}) 抑制編譯警告,應用於類型、構造器、方法、域、參數以及局部變數。 value是類型數組,有效取值為:

all, to suppress all warnings

boxing, to suppress warnings relative to boxing/unboxing operations

cast, to suppress warnings relative to cast operations

dep-ann, to suppress warnings relative to deprecated annotation

deprecation, to suppress warnings relative to deprecation

fallthrough, to suppress warnings relative to missing breaks in switch statements

finally, to suppress warnings relative to finally block that don't return

hiding, to suppress warnings relative to locals that hide variable

incomplete-switch, to suppress warnings relative to missing entries in a switch statement (enum case)

javadoc, to suppress warnings relative to javadoc warnings

nls, to suppress warnings relative to non-nls string literals

null, to suppress warnings relative to null analysis

rawtypes, to suppress warnings relative to usage of raw types

restriction, to suppress warnings relative to usage of discouraged or forbidden references

serial, to suppress warnings relative to missing serialVersionUID field for a serializable class

static-access, to suppress warnings relative to incorrect static access

static-method, to suppress warnings relative to methods that could be declared as static

super, to suppress warnings relative to overriding a method without super invocations

synthetic-access, to suppress warnings relative to unoptimized access from inner classes

unchecked, to suppress warnings relative to unchecked operations

unqualified-field-access, to suppress warnings relative to field access unqualified

unused, to suppress warnings relative to unused code and dead code

4、註解的定義

使用 @interface 關鍵字聲明一個註解

public @interface MyAnnotation1

註解中可以定義屬性

String name default 「defval」;

value是註解中的特殊屬性

註解中定義的屬性如果名稱為 value, 此屬性在使用時可以省寫屬性名

例如,聲明一個註解:

@Retention(RetentionPolicy.RUNTIME)

public @interface MyAnno1 {

String msg();

int value();

}

熱點內容
存儲對齊 發布:2024-12-22 00:01:07 瀏覽:783
相機存儲卡損壞 發布:2024-12-21 23:50:14 瀏覽:889
pythonldap模塊 發布:2024-12-21 23:39:29 瀏覽:216
115ftp 發布:2024-12-21 23:38:02 瀏覽:253
服務推薦演算法 發布:2024-12-21 23:32:55 瀏覽:530
手機載入配置信息失敗是什麼意思 發布:2024-12-21 23:31:16 瀏覽:652
為什麼伺服器魚塔不刷魚 發布:2024-12-21 23:29:37 瀏覽:277
sqlserverjson 發布:2024-12-21 23:21:47 瀏覽:890
手機如何設置超級滑屏密碼 發布:2024-12-21 23:05:34 瀏覽:445
錄歌安卓用什麼耳機好 發布:2024-12-21 22:51:01 瀏覽:813