Java Annotation

java Annotation

Java注解的使用大大地减少了重复性高的代码,且可以流程化一系列的操作。

注解作用域:

  • ANNOTATION_TYPE
    Annotation type declaration
  • CONSTRUCTOR
    Constructor declaration
  • FIELD
    Field declaration (includes enum constants)
  • LOCAL_VARIABLE
    Local variable declaration
  • METHOD
    Method declaration
  • PACKAGE
    Package declaration
  • PARAMETER
    Formal parameter declaration
  • TYPE
    Class, interface (including annotation type), or enum declaration
  • TYPE_PARAMETER
    Type parameter declaration
  • TYPE_USE
    Use of a type

参考java.lang.annotation.ElementType

注解保留策略:

  • CLASS
    Annotations are to be recorded in the class file by the compiler but need not be retained by the VM at run time.
  • RUNTIME
    Annotations are to be recorded in the class file by the compiler and retained by the VM at run time, so they may be read reflectively.
  • SOURCE
    Annotations are to be discarded by the compiler.

参考java.lang.annotation.RetentionPolicy


AbstractProcessor

  • Element
    Represents a program element such as a package, class, or method.

  • QualifiedNameable
    A mixin interface for an element that has a qualified name.

  • Parameterizable
    A mixin interface for an element that has type parameters.

  • PackageElement
    Represents a package program element.

  • TypeElement
    Represents a class or interface program element.

  • TypeParameterElement
    Represents a formal type parameter of a generic class, interface, method, or constructor element. A type parameter declares a TypeVariable.

  • VariableElement
    Represents a field, enum constant, method or constructor parameter, local variable, resource variable, or exception parameter.

  • ExecutableElement
    Represents a method, constructor, or initializer (static or instance) of a class or interface, including annotation type elements.


源码阶段的Annotation使用

使用方式:

  1. 定义注解
1
2
@Retention(SOURCE)
@Target(FIELD)
  1. 编译处理类
1
2
3
4
javac <extends AbstractProcessor .java file>.
```

3. 编译使用类

javac -processor <extends AbstractProcessor .java file> <annotation *.java files>.

1
2
3
4
5


**e.g.:**


package com.test;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.SOURCE;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(SOURCE)
@Target(FIELD)
public @interface Getter {

boolean include() default true;

}

1
2


package com.test;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Parameterizable;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.element.VariableElement;
import javax.tools.JavaFileObject;

@SupportedAnnotationTypes(“com.test.Getter”)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class GetterAnnotationProcessor extends AbstractProcessor {

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    if (annotations.size() == 0) {
        return true;
    }
    String newSourceFilePackage = "com.test";
    String newSourceClassName = "AutoGenCode";
    try {
        JavaFileObject sourceFile = processingEnv.getFiler()
                .createSourceFile(newSourceFilePackage + "." + newSourceClassName);
        PrintWriter writer = new PrintWriter(sourceFile.openWriter());
        // print code for package and class
        StringBuilder sb = new StringBuilder();
        sb.append("package ").append(newSourceFilePackage).append(";\n\n");
        sb.append("public class ").append(newSourceClassName).append(" {\n\n");

        sb.append(blank(4)).append("public ").append(newSourceClassName).append("() {}\n\n");

        int i = 0;
        for (Element e : roundEnv.getElementsAnnotatedWith(Getter.class)) {
            System.out.println("times:" + (i++));
            System.out.println("e:" + e);
            System.out.println("e instanceof TypeElement:" + (e instanceof TypeElement));
            System.out.println("e instanceof VariableElement:" + (e instanceof VariableElement));
            System.out.println("e instanceof Parameterizable:" + (e instanceof Parameterizable));
            System.out.println("e instanceof TypeParameterElement:" + (e instanceof TypeParameterElement));
            if (e instanceof VariableElement) {
                VariableElement ve = (VariableElement) e;
                writeTo(sb, ve);
            }
        }
        sb.append("\n}");

        System.out.println("sb:" + sb);

        writer.print(sb);

        writer.flush();
        writer.close();
    } catch (IOException e) {
        e.printStackTrace();
    }

    return false;
}

private void writeTo(StringBuilder sb, VariableElement e) {
    System.out.println("IN writeTo....");
    String fieldName = e.getSimpleName().toString();
    System.out.println("VariableElement.getSimpleName:" + fieldName);
    Getter ann = e.getAnnotation(Getter.class);
    if (ann != null && ann.include()) {
        sb.append(blank(4)).append("public String get");
        sb.append(fieldName);
        sb.append("() {\n");
        sb.append(blank(8)).append("return \"").append(fieldName).append("\";\n");
        sb.append(blank(4)).append("}\n\n");
    }
    System.out.println("OUT writeTo....");
}


private StringBuilder blank(int num) {
    StringBuilder sb = new StringBuilder();
    while(num--!=0) {
        sb.append(" ");
    }
    return sb;
}

public static void main(String[] args) {
    System.out.println(new GetterAnnotationProcessor().blank(4));
}

}

1
2
3



package com.test.example;

import com.test.Getter; //此处用eclipse会报错,不用理会

public class Test {

@Getter(include = true)
private String abc;

@Getter(include = false)
private String def;

@Getter(include = true)
private String ghi;

public static void main(String[] args) {
    
}

}

1

cd root path:

javac com/test/GetterAnnotationProcessor.java

javac -processor com.test.GetterAnnotationProcessor com/test/example/*.java

编译完成后,在"com/test/"目录下会发现 GetterAnnotationProcessor处理生成的类com.test.AutoGenCode。

1
2
3
4
5
6
7


------------
## 运行时阶段Annotation

运行时阶段的注解通常通过反射来获取相关信息。

package com.test.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**

  • @author w

/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FieldProperty {
/
*
* 字段名
*
* @return
*/
String name() default “”;

/**
 * 字段编号
 * 
 * @return
 */
int order() default -1;

}

1

package com.test;

import com.test.annotation.FieldProperty;

public class UseAnnotation {

@FieldProperty(name = "v1", order = 0)
private String var1;

private String var2;

public String getVar1() {
    return var1;
}

public void setVar1(String var1) {
    this.var1 = var1;
}

public String getVar2() {
    return var2;
}

public void setVar2(String var2) {
    this.var2 = var2;
}

}

1
2


package com.test;

import java.lang.reflect.Field;

import com.test.annotation.FieldProperty;

public class Test {

public static void test(Class<?> entity) throws Exception {

    Field[] fields = entity.getDeclaredFields();
    if (fields == null || fields.length <= 0) {
        return;
    }
    for (Field field : fields) {
        FieldProperty fieldProperties = field.getAnnotation(FieldProperty.class);
        if (fieldProperties == null) {
            throw new Exception(entity.getSimpleName() + " " + field.getName() + " annotation 异常.");
        }

        if (fieldProperties.order() == -1) {
            throw new Exception(entity.getSimpleName() + " " + field.getName() + " 字段未编号.");
        }

        System.out.println("name:" + fieldProperties.name() + ", order:" + fieldProperties.order());
    }

}

public static void main(String[] args) {
    try {
        test(UseAnnotation.class);
    } catch (Exception e) {
        e.printStackTrace();
    }

}

}

1
2

运行以上Test会得到类似以下的结果:

java.lang.Exception: UseAnnotation var2 annotation 异常.
name:v1, order:0
at com.test.Test.test(Test.java:18)
at com.test.Test.main(Test.java:32)



------------
## 参考  
- [1] [Maven annotation](https://deors.wordpress.com/2011/10/08/annotation-processors/)