注釋是一些標簽,我們可以將其插入程序源代碼中,以使用某種工具對其進行處理并使其變得有意義。 注釋處理工具通常使用(Java 5 SE的)Reflection API在Java代碼或字節碼級別的源代碼級別處理代碼,以處理編譯器已將注釋放入其中的類文件。 Java注釋在網絡上的許多地方都得到了很好的解釋,但唯一能找到一個明智而完整的示例的地方是Prentice Hall出版的一本精裝書,名為Core Java:Volume II – Advanced Features,由Cay S. Horstmann撰寫。和加里·康奈爾。
網絡上幾乎所有試圖解釋注釋的地方都缺少為我們展示用于自定義書面注釋的注釋處理工具(APT)以及從代碼中使用注釋的最關鍵部分。 我已使用本書中的信息構建了一些注釋,用于驗證變量并從項目的屬性文件中初始化變量中的值。 我對www上缺少編寫自定義Java注釋的示例的觀察,促使我寫這篇文章。 因此,向您提供一個示例自定義Java注釋,以幫助您為自己可能要做的事情編寫自己的注釋。
我將帶您遍歷NullValueValidate
批注,其名稱所暗示的目的是驗證其注釋的變量包含非null值。 如果在處理時發現null值,則將拋出NullPointerException
。
聲明注釋
讓我們首先聲明我們的注釋。 該聲明將由打算使用注釋來注釋其對象中變量的代碼使用。
package annotation.declaration;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** Null Value Validate is, as the name suggests an annotation to* validate whether the parameter is null or not* @author Y.Kamesh Rao**/
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)public @interface NullValueValidate {String paramName();
}
注意“ interface”關鍵字前面的“ @”(AT)符號。 這是用于聲明注釋的語法。 這稱為注釋接口 。 界面的方法對應于注釋的元素。 paramName()
–這是我們的注釋聲明包含的唯一元素。 它存儲帶注釋的字段的名稱,以便在處理時在消息中顯示它。 請注意,該聲明看起來像一個函數聲明。 其實就是這樣。 @interface
實際上聲明了一個Java接口,該接口的實現由使用注釋的對象提供。 注釋處理器接收使用/實現注釋的對象,并調用注釋接口方法以檢索注釋元素。 在我們的例子中, NullValueValidateAnnotationProcessor
將接收該類的對象,該類的某些字段使用NullValueValidate
注釋進行了注釋。 然后,該處理器將調用paramName()
方法來檢索此注釋元素的值。
我們使用3種Java提供的注釋來注釋聲明的屬性。 這些也可以稱為“ 內置注釋” ,用于“注釋” 。 (嗯,繞口令比這強得多)。 @Documented
–表示在使用JavaDocs為此項目創建文檔時,必須包含注釋聲明。 缺省情況下,注釋是從使用javadocs
命令生成的文檔中排除的。 @Target
–指示您的Java程序中應對其應用注釋的目標元素。 它可以是字段,方法,類或整個包本身。 我們的NullValueValidate
注釋僅適用于類字段。 這是此枚舉可能取的值–
- TYPE –僅適用于Type。 類型可以是Java類或接口,也可以是Enum甚至是Annotation。
- FIELD –僅適用于Java字段(在類級別聲明的對象,實例或靜態)。
- 方法–僅適用于方法。
- 參數–僅應用于方法定義中的方法參數。
- 構造函數–僅適用于類的構造函數。
- LOCAL_VARIABLE –僅適用于局部變量。 (在方法或代碼塊中聲明的變量)。
- ANNOTATION_TYPE –僅適用于注釋類型。
- 包裝-僅適用于包裝。
@Retention
–指示用于注釋的保留策略。 簡單來說,我們將保留注釋很長時間。 有三個可能的值–
- 源–注釋將被編譯器丟棄。
- CLASS –注釋由編譯器記錄在類文件中,但VM在運行時無需保留。 這是默認行為。
- RUNTIME –注釋由編譯器記錄在類文件中,并在運行時由VM保留,因此可以通過反射方式讀取它們。
由于我們計劃在程序運行時處理批注,因此將RetentionPolicy
設置為RUNTIME
。 @Target
和@Retention
也稱為元注釋。
注釋處理工具
注釋處理器工具,解析它接收的對象,并在仔細檢查對象中找到正在處理的注釋時采取編程的措施。 這是我們先前聲明的注釋的注釋處理器– NullValueValidate
。
package annotation.processor;import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import annotation.declaration.NullValueValidate;/*** The class file to actually carry out the validations* for the various validate annotations we have declared* @author Y.Kamesh Rao*/
public class NullValueValidateAnnotationProcessor {/*** Method to process all the annotations* @param obj The name of the object where* annotations are to be identified and* processed*/public static void processAnnotations(Object obj) {try {Class cl = obj.getClass();// Checking all the fields for annotationsfor(Field f : cl.getDeclaredFields()) {// Since we are Validating fields, there may be many// NullPointer and similar exceptions thrown,// so we need to catch themtry {// Processing all the annotations on a single fieldfor(Annotation a : f.getAnnotations()) {// Checking for a NullValueValidate annotationif(a.annotationType() == NullValueValidate.class) {NullValueValidate nullVal = (NullValueValidate) a;System.out.println('Processing the field : '+ nullVal.paramName());// Setting the field to be accessible from our class// is it is a private member of the class under processing// (which its most likely going to be)// The setAccessible method will not work if you have// Java SecurityManager configured and active.f.setAccessible(true);// Checking the field for a null value and// throwing an exception is a null value encountered.// The get(Object obj) method on Field class returns the// value of the Field for the Object which is under test right now.// In other words, we need to send 'obj' as the object// to this method since we are currently processing the// annotations present on the 'obj' Object.if(f.get(obj) == null) {throw new NullPointerException('The value of the field '+f.toString()+' can't be NULL.');} elseSystem.out.println('Value of the Object : '+f.get(obj));}}} catch(Exception e) {System.out.println(e.getMessage());e.printStackTrace();}}} catch(Exception e) {System.out.println(e.getMessage());e.printStackTrace();}}
}
大部分代碼是帶有注釋的自我解釋。 請參考該代碼以了解詳細信息。 基本上,它具有一個稱為processAnnotations
的靜態方法,該方法采用包含需要處理的注釋的類的對象。 然后,我們使用Java Reflection API處理此接收到的對象參數中的每個Field,并在字段上找到NullValueValidate
注釋時采取必要的空值驗證操作。 如果找到空值,則拋出NullPointerException
或在控制臺上打印該值。
注釋用法請參考以下代碼,該代碼使用我們剛剛實現的NullValueValidate
注釋。 它也使用NullValueValidateAnnotationProcessor
在運行時通過從其構造函數調用它在其字段上處理聲明的注釋。 還要注意,注釋的使用方式與變量或字段聲明的訪問修飾符(如private或public)相似。 通常,輸入換行符可以提高代碼的可讀性。 否則,注釋可以很好地與變量/字段聲明在同一行中。 注釋的名稱前面帶有“ @”(AT)符號。
package annotation;import annotation.declaration.NullValueValidate;
import annotation.processor.NullValueValidateAnnotationProcessor;/** Main class to test the Annotations * @author Y.Kamesh Rao */
public class AnnotationExample {@NullValueValidate(paramName = 'testVar1') private String testVar1;@NullValueValidate(paramName = 'testVar2') private String testVar2;public AnnotationExample() {testVar2 = 'Testing the Null Value Validation...It Works...!'; // Calling the processor to process the annotations applied // on this class object. NullValueValidateAnnotationProcessor.processAnnotations(this); } public static void main(String args[]) {AnnotationExample ae = new AnnotationExample(); }
}
輸出量
Processing the field:testVar1
Value of the Object:Testing the Null Value Validation...It Works...!
Processing the field:testVar2
The value of the field private java.lang.String annotation.AnnotationExample.testVar2 cannot be NULL.
java.lang.NullPointerException:The value of the field private java.lang.String annotation.AnnotationExample.testVar2 cannot be NULL.at annotation.processor.NullValueValidateAnnotationProcessor.processAnnotation
(NullValueValidateAnnotationProcessor.java:66)at annotation.AnnotationExample.(AnnotationExample.java:28)at annotation.AnnotationExample.main(AnnotationExample.java:33)
結論
我做這個示例注釋程序很有趣,現在我實現了許多自定義注釋,以從屬性文件中加載屬性,驗證數據庫字段長度等。注釋極大地減少了代碼的冗長性,因此使其更加簡單易讀。 注釋可用于日志記錄,生成依賴于代碼的部署描述符以及其他機械和重復的作業。 我為你們編寫這篇文章帶來了很多樂趣。 希望您能從中受益。
參考: Java注釋:我們的JCG合作伙伴 Y Kamesh Rao在OrangeApple博客上進行了探索和解釋 。
翻譯自: https://www.javacodegeeks.com/2012/08/java-annotations-explored-explained.html