Java Annotation
Table of Contents
1. Java Annotation
1.1. 定义 annotation
@Target(ElementType.TYPE) @Retention(RetentionPolicy.SOURCE) public @interface ClassAuthor { String value() default "sunway"; } @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MethodAuthor { String name() default "sunway"; int version(); }
1.1.1. @Target
使用 @Target 定义该 annotation 可以用在哪里:
- CONSTRUCTOR
- FIELD
- LOCAL_VARIABLE
- METHOD
- PACKAGE
- PARAMETER
- TYPE (包括 class, interface, enum)
例如:
@TypeAnnotation public class MyAnnotatedClass { @FieldAnnotation private String foo; @ConstructorAnnotation public MyAnnotatedClass() { } @MethodAnnotation public String bar(@ParameterAnnotation String str) { @LocalVariableAnnotation String asdf = "asdf"; return asdf + str; } }
1.1.2. @Retention
@Retention 定义该 annotation 信息如何保存
RetentionPolicy.SOURCE
annotation 保存在源码中, 但不保留在 .class 中. 这种 annotation 通常是通过 annotation processor 来处理
RetentionPolicy.CLASS
annotation 保存在 .class 中, 但 jvm 运行时并不会加载它们. 直接分析 .class 文件的编译时工具可以处理这类的 annotation, 例如 findbugs
RetentionPolicy.RUNTIME
annotation 保存在 .class 中, 且 jvm 运行时会加载它们, 所以可以在运行时通过反射得到.
1.1.3. @interface
使用 @interface 来定义 annotation 的内容, 与定义一个接口的语法类似, 且最终会编译生成一个具体的 class, 其中 value() 是一个特殊的方法, 参考
1.2. 使用 annotation
@ClassAuthor("sunway") class Hello { @MethodAuthor (name = "sunway", version = 1) public void foo() {} }
若 annotation 定义了 value() 方法, 则可以使用 ClassAuthor 那种写法: @annotation(value), 否则需要使用 MethodAuthor 那种写法: @annotation(key1=value1,key1=value2)
1.3. 运行时 annotation
对于 RetentionPolicy 为 RUNTIME 的 annotation, 可以在运行时通过反射来处理
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface RuntimeAuthor { String name() default "sunway"; int age() default 0; } @RuntimeAuthor (name = "sunway", age = 16) class HelloAnnotation3 {} public class HelloAnnotation { public static void main(String argv[]) { for (Annotation a : HelloAnnotation3.class.getAnnotations()) { if (a instanceof RuntimeAuthor) { System.out.println(((RuntimeAuthor)a).name()); System.out.println(((RuntimeAuthor)a).age()); } } } }
1.4. 编译时 annotation
所有 annotation 都可以在编译时通过 annotation processor 处理, 包括 RetentionPolicy.{RUNTIME,CLASS}, 因为它们也保留在源码中
1.4.1. 使用 annotation processor
定义 processor
- 后续使用 processor时, process 可能会被调用多次.
- 通过 printMessage, 可以打印出 warning 或 error, 中断编译
- 可以编译时生成 java 源文件, 生成的文件会参与到后续的编译中
@SupportedSourceVersion(SourceVersion.RELEASE_8) @SupportedAnnotationTypes({"Author"}) public class MyProcessor extends AbstractProcessor { @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { if (annotations.isEmpty()) { return true; } System.out.println("process"); StringBuilder builder = new StringBuilder() .append("import java.util.HashMap;") .append("public class GeneratedAuthors {\n") .append(" private static HashMap<String,String> sAuthors=new HashMap(); ") .append(" public static String dump() {return sAuthors.toString();}") .append(" static {"); Map<String, String> authors = new HashMap(); for (TypeElement annotation : annotations ) { for ( Element element : roundEnv.getElementsAnnotatedWith(annotation) ) { Author author = element.getAnnotation(Author.class); processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "found @Author at " + element + " value: " + author.value()); RuntimeAuthor runtimeAuthor = element.getAnnotation(RuntimeAuthor.class); if (runtimeAuthor != null) { processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "found @RuntimeAuthor at " + element + " value: " + runtimeAuthor.name()); } // process 可以中断编译 if (!author.value().equals("sunway")) { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "author must be sunway"); } authors.put(element.toString(), author.value()); builder.append("sAuthors.put(") .append("\"" + element.toString() + "\"," + "\"" + author.value() + "\"" + ");"); } } builder.append("}}"); // generate code try { JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile( "GeneratedAuthors"); PrintWriter out = new PrintWriter(sourceFile.openWriter()); out.write(builder.toString()); out.close(); } catch (Exception e) { } return true; } }
使用编译完的 processor
javac -cp . -processor MyProcessor *.java