1、注解保留策略
public enum RetentionPolicy {
/**
* 注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃.
* 这意味着:Annotation仅存在于编译器处理期间,编译器处理完之后,该Annotation就没用了
*/
SOURCE,
/**
* 注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期.
*/
CLASS,
/**
* 注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在,
* 保存到class对象中,可以通过反射来获取
*/
RUNTIME
}
2、注解能用到哪里
public enum ElementType {
/**
* 类、接口(包括注解类型)或枚举声明
*/
TYPE,
/**
* 注解类型声明
*/
ANNOTATION_TYPE,
/**
* 字段声明(包括枚举常量)
*/
FIELD,
/**
* 方法声明
*/
METHOD,
/**
* 参数声明
*/
PARAMETER,
/**
* 构造方法声明
*/
CONSTRUCTOR,
/**
* 局部变量声明
*/
LOCAL_VARIABLE,
/**
* 包声明
*/
PACKAGE
}
3、和注解有关的Java原生API
java.lang.Class<T>
public <A extends Annotation> A getAnnotation(Class<A> annotationClass)
如果存在该元素的指定类型的注解,则返回这些注解,否则返回 null。
Annotation[] getAnnotations()
返回此元素上存在的所有注解。
Annotation[] getDeclaredAnnotations()
返回直接存在于此元素上的所有注解。
boolean isAnnotation()
如果此 Class 对象表示一个注解类型则返回 true。
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
如果指定类型的注解存在于此元素上,则返回 true,否则返回 false。
java.lang.reflect.Constructor<T>
<T extends Annotation> T getAnnotation(Class<T> annotationClass)
如果存在该元素的指定类型的注解,则返回这些注解,否则返回 null。
Annotation[] getDeclaredAnnotations()
返回直接存在于此元素上的所有注解。
java.lang.reflect.Field
<T extends Annotation> T getAnnotation(Class<T> annotationClass)
如果存在该元素的指定类型的注解,则返回这些注解,否则返回 null。
Annotation[] getDeclaredAnnotations()
返回直接存在于此元素上的所有注解。
java.lang.reflect.Method
<T extends Annotation> T getAnnotation(Class<T> annotationClass)
如果存在该元素的指定类型的注解,则返回这些注解,否则返回 null。
Annotation[] getDeclaredAnnotations()
返回直接存在于此元素上的所有注解。
Annotation[][] getParameterAnnotations()
返回表示按照声明顺序对此 Method 对象所表示方法的形参进行注解的那个数组的数组。
4、小试牛刀
该Demo参考了这篇博客。
假设你想模仿org.junit.Test写一个自己的注解类。
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.METHOD) // 该注解只能用在方法签名上面
public @interface Test {
// 若值为false,表示使用该注解的那个方法将被忽略,即不想被执行
boolean enabled() default true;
}
假设你还想自定义一个注解,用于标识测试人的一些信息。
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 TesterInfo {
enum Priority {
LOW, MEDIUM, HIGH
}
Priority priority() default Priority.MEDIUM;
String[] tags() default "";
String createdBy() default "Liu Dehua";
String lastModified() default "2019-06-03 18:00:00";
}
展示上述两个自定义注解的用法。
@TesterInfo(
priority = Priority.HIGH,
createdBy = "Xiaoming",
tags = {"sales","test" },
lastModified = "2019-6-03 15:28:00"
)
public class TestExample {
@Test
void testA() {
if (true)
throw new RuntimeException("This test always failed");
}
/**
* 通过enabled = false告知注解解释器不要执行
*/
@Test(enabled = false)
void testB() {
if (false)
throw new RuntimeException("This test always passed");
}
@Test(enabled = true)
void testC() {
if (10 > 1) {
// 省略
}
}
}
无论注解设计的多么完美,总需要一个“懂它”的注解解释器。
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
public class AnnotationInterpreter {
public static void main(String[] args) throws Exception {
System.out.println("Testing...");
int passed = 0, failed = 0, count = 0, ignore = 0;
// 硬编码.class文件不太好,项目中更常见的是扫描某些包中的所有类
Class<TestExample> obj = TestExample.class;
// 处理TesterInfo注解
if (obj.isAnnotationPresent(TesterInfo.class)) {
Annotation annotation = obj.getAnnotation(TesterInfo.class);
TesterInfo testerInfo = (TesterInfo) annotation;
System.out.printf("%nPriority :%s", testerInfo.priority());
System.out.printf("%nCreatedBy :%s", testerInfo.createdBy());
System.out.printf("%nTags :");
int tagLength = testerInfo.tags().length;
for (String tag : testerInfo.tags()) {
if (tagLength > 1) {
System.out.print(tag + ", ");
} else {
System.out.print(tag);
}
tagLength--;
}
System.out.printf("%nLastModified :%s%n%n", testerInfo.lastModified());
}
// 处理自定义的Test注解,注意不是org.junit.Test
for (Method method : obj.getDeclaredMethods()) {
if (method.isAnnotationPresent(Test.class)) {
Annotation annotation = method.getAnnotation(Test.class);
Test test = (Test) annotation;
if (test.enabled()) {
try {
method.invoke(obj.newInstance());
System.out.printf("%s - Test '%s' - passed %n", ++count, method.getName());
passed++;
} catch (Throwable ex) {
System.out.printf("%s - Test '%s' - failed: %s %n", ++count, method.getName(), ex.getCause());
failed++;
}
} else {
System.out.printf("%s - Test '%s' - ignored%n", ++count, method.getName());
ignore++;
}
}
}
System.out.printf("%nResult : Total : %d, Passed: %d, Failed %d, Ignore %d%n", count, passed, failed, ignore);
}
}
执行结果: