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

  1. 定义 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;
        }
    
    }
    
    
  2. 使用编译完的 processor

    javac -cp . -processor MyProcessor *.java
    

Author: [email protected]
Date: 2019-07-11 Thu 00:00
Last updated: 2024-02-01 Thu 14:04

知识共享许可协议