1 Java 元注解
元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。Java5.0定义的元注解:
- 1.@Target,
- 2.@Retention,
- 3.@Documented,
- 4.@Inherited
这些类型和它们所支持的类在java.lang.annotation包中可以找到。下面我们看一下每个元注解的作用和相应分参数的使用说明。
@Target
@Target说明了Annotation所修饰的对象范围,默认可以使用在任何地方:
用法 | 使用范围 |
---|---|
@Target(ElementType.METHOD) | 用于方法 |
@Target(ElementType.TYPE) | 用于类或者接口 |
@Target(ElementType.ANNOTATION_TYPE) | 用于注解类型(被@interface修饰的类型) |
@Target(ElementType.CONSTRUCTOR) | 用于构造方法 |
@Target(ElementType.FIELD) | 用于域 |
@Target(ElementType.LOCAL_VARIABLE) | 用于局部变量 |
@Target(ElementType.PACKAGE) | 记录java文件的package信息 |
@Target(ElementType.PARAMETER) | 用于参数 |
在Annotation类型的声明中使用了target可更加明晰其修饰的目标。
@Retention
@Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。
作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)
取值(RetentionPoicy)有:
取值 | 有效范围 |
---|---|
@Retention(RetentionPolicy.SOURCE) | 在源文件中有效(即源文件保留) |
@Retention(RetentionPolicy.CLASS) | 在class文件中有效(即class保留) |
@Retention(RetentionPolicy.RUNTIME) | 在运行时有效(即运行时保留) |
Column注解的的RetentionPolicy的属性值是RUTIME,这样注解处理器可以通过反射,获取到该注解的属性值,从而去做一些运行时的逻辑处理。
@Documented
@Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。
@Inherited
@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。
当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。
自定义注解
下面写一个从*.properties
文件中读取配置,来演示如何开发一个自定义注解。
我们需要定义两个注解,第一个用来从路径中读取配置文件*.properties
定义为@Source
,第二个用来给变量赋值,定义为@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) //在运行时有效
@Target(ElementType.TYPE) //用于类或者接口
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)//在运行时有效
@Target(ElementType.FIELD)//用于域
public @interface Value {
String value();
}
下面演示如何工作。
test-annotation.properties
hello=annotation
TestAnnotation.java
//使用@Source标记配置文件来源
@Source(value = "src/main/resources/test-annotation.properties")
public class TestAnnotation {
//使用@Value标记读取的属性
@Value(value = "hello")
public static String hello;
public static void main(String[] args) {
AnnotationWork.work(TestAnnotation.class);
System.out.println(hello);
}
}
结果为
annotation
重点就在于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赋予了初始值。