湖畔镇

Java——注解

注解为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻使用这些数据

基本语法

public class Testable {
    public void execute() {
        ...
    }

    @Test
    void testExecute() {
        execute();
    }
}

定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {}

@Target定义你的注解用于什么地方,@Retention定义该注解在哪个级别可用

注解一般会包含一些元素以表示某些值

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
    public int id();
    public String description() default "no description";
}

下面的类中有两个方法被注解为用例

public class PasswordUtil {

    @UseCase(id = 47, description = "passwords must contain at least on numeric")
    public boolean validatePassword(String password) {
        ...
    }

    @UseCase(id = 48)
    public String encryptPassword(String password) {
        ...
    }
}

元注解

@Target

注解用于什么地方

  • CONSTRUCTOR
  • FIELD
  • LOCAL_VARIABLE
  • METHOD
  • PACKAGE
  • PARAMETER
  • TYPE

@Retention

在什么级别保存信息

  • SOURCE
  • CLASS
  • RUNTIME

@Documented

此注解中包含在Javadoc中

@Inherited

允许子类继承父类中的注解

注解处理器

public class UseCaseTracker {
    public static void trackUseCases(Class<?> clazz) {
        for (Method m : clazz.getDeclaredMethods()) {
            UseCase uc = m.getAnnotation(UseCase.class);
            ......
        }
     }
}

使用两个反射方法getDeclaredMethods()getAnnotation()

注解元素

可以使用下面类型:

  • 所有基本类型
  • String
  • Class
  • enum
  • Annotation
  • 以上类型的数组

不允许使用包装类型,注解可以嵌套

默认值限制

元素不能有不确定的值,要么有默认值,要么注解时提供

对非基本类型元素,不能以null为值,只能自己定义特殊值如空字符串或负数以表明元素不存在

生成外部文件

假设你希望提供一些基本的对象-关系映射功能,能够自动生成数据库表,用以存储JavaBean对象,可以通过注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBTable {
    public String name() default "";
}

生成数据库表的注解

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Constraints {
    boolean primaryKey() default false;
    boolean allowNull() default true;
    boolean unique() default false;
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLString {
    int value() default 0;
    String name() default "";
    Constraints constraints() default @Constraints;
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLInteger {
    String name() default "";
    Constraints constraints() default @Constraints;
}

数据库的约束,以及SQL类型,其中用到了嵌套注解

@DBTable(name = "MEMBER")
public class Member {
    @SQLString(30)
    String firstName;

    @SQLString(50)
    String lastName;

    @SQLInteger
    Integer age;

    @SQLString(value = 30, constraints = @Constraints(primaryKey = true))
    String handle;
}

一个简单的JavaBean

注解不支持继承

使用apt处理注解

apt操作源文件而不是编译后的类

通过使用AnnotationProcessorFactoryapt可以为每一个它发现的注解生成一个正确的注解处理器

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface ExtractInterface {
    public String value();
}

一个把类中的public方法提取出来的注解

@ExtractInterface("IMultiplier")
public class Multiplier {
    public int multiply(int x, int y) {
        ...
    }

    private int add(int x, int y) {
        ...
    }
}

用它来注解这个乘法运算类

public class InterfaceExtractorProcessor implements AnnotationProcessor {

    private List<MethodDeclaration> interfaceMethods = new ArrayList<MethodDeclaration>();

    public InterfaceExtractorProcessor(AnnotationProcessorEnvironment env) {
        this.env = env;
    }

    public void process() {
        for (TypeDeclaration typeDecl : env.getSpecifiedTypeDeclarations()) {
            ExtractInterface annot = typeDecl.getAnnotation(ExtractInterface.class);
            if (annot == null) {
                break;
            }

            for (MethodDeclaration m : typeDecl.getMethods()) {
                if (m.getModifiers().contains(Modifier.PUBLIC)) {
                    interfaceMethods.add(m);
                }
            }

            if (interfaceMethods.size() > 0) {
                PrintWriter writer = env.getFiler().createSourceFile(annot.value());
                writer.println("package " + typeDecl.getPackage().getQualifiedName() + ";");
                writer.println("public interface " + annot.value()) + " {");
                for (MethodDeclaration m : interfaceMethods) {
                    writer.print("  public ");
                    writer.print(m.getReturnType() + " ");
                    writer.print(m.getSimpleName() + " (");
                    int i = 0;
                    for (ParameterDeclaration param : m.getParameters()) {
                        writer.print(param.getType() + " " + param.getSimpleName());
                        if (++i < m.getParameters().size()) {
                            writer.print(", ");
                        }
                        writer.println(");");
                    }
                    writer.println("}");
                    writer.close();
                }
            }
        }
    }
}

所有工作都在process()中完成,构造器以AnnotationProcessorEnvironment为参数,可以知道apt正在处理的所有类型,可以获得Messenger对象和Filer对象,前者用来报告消息,后者用来创建新文件

public class InterfaceExtractorProcessorFactory implements AnnotationProcessorFactory {

    public Collection<String> supportedOptions() {

    }

    public AnnotationProcessor getProcessorFor(Set<AnnotationTypeDeclaration> atds, AnnotationProcessorEnvironment env) {

    }

    public Collection<String> supportedAnnotationTypes() {

    }
}

apt工具需要一个工厂类来指明正确的处理器

apt -factory annotations.InterfaceExtractorProcessorFactory Multiplier.java -s ../annotations
分享