1 Java 元注解
元注解的作用就是負(fù)責(zé)注解其他注解。Java5.0定義了4個(gè)標(biāo)準(zhǔn)的meta-annotation類(lèi)型,它們被用來(lái)提供對(duì)其它 annotation類(lèi)型作說(shuō)明。Java5.0定義的元注解:
- 1.@Target,
- 2.@Retention,
- 3.@Documented,
- 4.@Inherited
這些類(lèi)型和它們所支持的類(lèi)在java.lang.annotation包中可以找到。下面我們看一下每個(gè)元注解的作用和相應(yīng)分參數(shù)的使用說(shuō)明。
@Target
@Target說(shuō)明了Annotation所修飾的對(duì)象范圍,默認(rèn)可以使用在任何地方:
| 用法 | 使用范圍 |
|---|---|
| @Target(ElementType.METHOD) | 用于方法 |
| @Target(ElementType.TYPE) | 用于類(lèi)或者接口 |
| @Target(ElementType.ANNOTATION_TYPE) | 用于注解類(lèi)型(被@interface修飾的類(lèi)型) |
| @Target(ElementType.CONSTRUCTOR) | 用于構(gòu)造方法 |
| @Target(ElementType.FIELD) | 用于域 |
| @Target(ElementType.LOCAL_VARIABLE) | 用于局部變量 |
| @Target(ElementType.PACKAGE) | 記錄java文件的package信息 |
| @Target(ElementType.PARAMETER) | 用于參數(shù) |
在Annotation類(lèi)型的聲明中使用了target可更加明晰其修飾的目標(biāo)。
@Retention
@Retention定義了該Annotation被保留的時(shí)間長(zhǎng)短:某些Annotation僅出現(xiàn)在源代碼中,而被編譯器丟棄;而另一些卻被編譯在class文件中;編譯在class文件中的Annotation可能會(huì)被虛擬機(jī)忽略,而另一些在class被裝載時(shí)將被讀取(請(qǐng)注意并不影響class的執(zhí)行,因?yàn)锳nnotation與class在使用上是被分離的)。使用這個(gè)meta-Annotation可以對(duì) Annotation的“生命周期”限制。
作用:表示需要在什么級(jí)別保存該注釋信息,用于描述注解的生命周期(即:被描述的注解在什么范圍內(nèi)有效)
取值(RetentionPoicy)有:
| 取值 | 有效范圍 |
|---|---|
| @Retention(RetentionPolicy.SOURCE) | 在源文件中有效(即源文件保留) |
| @Retention(RetentionPolicy.CLASS) | 在class文件中有效(即class保留) |
| @Retention(RetentionPolicy.RUNTIME) | 在運(yùn)行時(shí)有效(即運(yùn)行時(shí)保留) |
Column注解的的RetentionPolicy的屬性值是RUTIME,這樣注解處理器可以通過(guò)反射,獲取到該注解的屬性值,從而去做一些運(yùn)行時(shí)的邏輯處理。
@Documented
@Documented用于描述其它類(lèi)型的annotation應(yīng)該被作為被標(biāo)注的程序成員的公共API,因此可以被例如javadoc此類(lèi)的工具文檔化。Documented是一個(gè)標(biāo)記注解,沒(méi)有成員。
@Inherited
@Inherited 元注解是一個(gè)標(biāo)記注解,@Inherited闡述了某個(gè)被標(biāo)注的類(lèi)型是被繼承的。如果一個(gè)使用了@Inherited修飾的annotation類(lèi)型被用于一個(gè)class,則這個(gè)annotation將被用于該class的子類(lèi)。
注意:@Inherited annotation類(lèi)型是被標(biāo)注過(guò)的class的子類(lèi)所繼承。類(lèi)并不從它所實(shí)現(xiàn)的接口繼承annotation,方法并不從它所重載的方法繼承annotation。
當(dāng)@Inherited annotation類(lèi)型標(biāo)注的annotation的Retention是RetentionPolicy.RUNTIME,則反射API增強(qiáng)了這種繼承性。如果我們使用java.lang.reflect去查詢(xún)一個(gè)@Inherited annotation類(lèi)型的annotation時(shí),反射代碼檢查將展開(kāi)工作:檢查class和其父類(lèi),直到發(fā)現(xiàn)指定的annotation類(lèi)型被發(fā)現(xiàn),或者到達(dá)類(lèi)繼承結(jié)構(gòu)的頂層。
自定義注解
下面寫(xiě)一個(gè)從*.properties文件中讀取配置,來(lái)演示如何開(kāi)發(fā)一個(gè)自定義注解。
我們需要定義兩個(gè)注解,第一個(gè)用來(lái)從路徑中讀取配置文件*.properties定義為@Source,第二個(gè)用來(lái)給變量賦值,定義為@Value。
Source.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) //在運(yùn)行時(shí)有效
@Target(ElementType.TYPE) //用于類(lèi)或者接口
public @interface Source {
String value() default "src/main/resources/*.properties";
}
Value.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)//在運(yùn)行時(shí)有效
@Target(ElementType.FIELD)//用于域
public @interface Value {
String value();
}
下面演示如何工作。
test-annotation.properties
hello=annotation
TestAnnotation.java
//使用@Source標(biāo)記配置文件來(lái)源
@Source(value = "src/main/resources/test-annotation.properties")
public class TestAnnotation {
//使用@Value標(biāo)記讀取的屬性
@Value(value = "hello")
public static String hello;
public static void main(String[] args) {
AnnotationWork.work(TestAnnotation.class);
System.out.println(hello);
}
}
結(jié)果為
annotation
重點(diǎn)就在于AnnotationWork.load(TestAnnotation.class);這行。
AnnotationWork.java
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Properties;
public class AnnotationWork {
public static void work(Class clazz){
if(clazz.isAnnotationPresent(Source.class)){//判斷class是否有@Source注解
String propertiesSource = ((Source) clazz.getAnnotation(Source.class)).value();
try {
Properties properties = new Properties();
properties.load(new FileReader(propertiesSource));
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if(field.isAnnotationPresent(Value.class)) {//判斷屬性屬否有@Value注解
String fieldKey = field.getAnnotation(Value.class).value();
field.set(clazz.newInstance(), properties.get(fieldKey));
}
}
}catch (IOException | IllegalAccessException | InstantiationException e){
e.printStackTrace();
}
}
}
}
代碼用反射的方法給hello賦予了初始值。