您的位置:首页 > 其它

注解(annotation) 2___使用apt 处理注解

2015-01-16 00:46 351 查看
注解处理工具 apt是为了帮助注解的处理过程而提供的工具.

1. 与javac 一样, apt 被 设计为操作Java源文件, 默认情况下, apt 会在处理完源文件后编译它们.

2. 当注解处理器生成一个新的源文件时, 该文件会在新一轮的注解处理中接受检查。 apt工具会一轮一轮地处理,直到不再有新的源文件产生为止。然后编译所有的源文件.

3. 开发人员自定义的每一个注解都需要自己的处理器, apt 工具能够很容易地将多个注解处理器组合在一起。

下面看代码InterfaceExtractorProcessor 类,要编译这个类出现的注解处理器, 必须将 tools.jar 设置在你的classpath , 这个工具类库同时还包含了com.sun.mirror.* 接口.

//: annotations/InterfaceExtractorProcessor.java
// APT-based annotation processing.
// {Exec: apt -factory
// annotations.InterfaceExtractorProcessorFactory
// Multiplier.java -s ../annotations}
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;

import com.sun.mirror.apt.AnnotationProcessor;
import com.sun.mirror.apt.AnnotationProcessorEnvironment;
import com.sun.mirror.declaration.MethodDeclaration;
import com.sun.mirror.declaration.Modifier;
import com.sun.mirror.declaration.ParameterDeclaration;
import com.sun.mirror.declaration.TypeDeclaration;

public class InterfaceExtractorProcessor implements AnnotationProcessor {
    
    private final AnnotationProcessorEnvironment env;
    private ArrayList<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) && !(m.getModifiers().contains(Modifier.STATIC)))
                    interfaceMethods.add(m);
            if (interfaceMethods.size() > 0) {
                try {
                    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 parm : m.getParameters()) {
                            writer.print(parm.getType() + " " + parm.getSimpleName());
                            if (++i < m.getParameters().size())
                                writer.print(", ");
                        }
                        writer.println(");");
                    }
                    writer.println("}");
                    writer.close();
                } catch (IOException ioe) {
                    throw new RuntimeException(ioe);
                }
            }
        }
    }
}


所有的工作都在process() 方法中完成. 在分析一个类的时候,用 MethodDeclaration 类以及其上的 getModifiers () 方法来找到public 方法(不包括static 的方法)。一旦找到我们所需的public 方法,就将其保存在一个 ArrayList中,然后在一个 java文件中, 创建新的接口中的方法定义.

(1) 处理器类的构造器以AnnotationProcessorEnvironment 对象为参数。 通过该对象,我们就能知道 apt 正在处理的所有类型(类定义),并且可以通过它获得Messager 对象和 Filer 对象. Messager 对象可以用来向用户报告 信息, 比如处理过程中发生的任何错误,以及错识在源代码中出现的位置等。 Filer 是一种PrintWriter, 我们可以通过它创建新的文件。 不使用普通的 PrintWriter
而使用 Filer 对象的主要原因是, 只有这样 apt 才能知道我们创建的新文件,从而对新文件进行注解处理,并且在需要的时候编译它们.

(2) Filer 的createSourceFile() 以将要新建的类或接口的名字,打开了一个普通的输出流。 现在还没有什么工具可以创建 Java 语言结构, 这里只能用最基本的print() 和println () 方法来生成 Java 源代码.

再看一个类InterfaceExtractorProcessorFactory ,实现了AnnotationProcessorFactory, apt 能够为每一个它发现的注解生成一个正确的注解处理器.

//: annotations/InterfaceExtractorProcessorFactory.java
// APT-based annotation processing.
import com.sun.mirror.apt.*;
import com.sun.mirror.declaration.*;
import java.util.*;

public class InterfaceExtractorProcessorFactory
  implements AnnotationProcessorFactory {
  public AnnotationProcessor getProcessorFor(
    Set<AnnotationTypeDeclaration> atds,
    AnnotationProcessorEnvironment env) {
    return new InterfaceExtractorProcessor(env);
  }
  public Collection<String> supportedAnnotationTypes() {
    return
     Collections.singleton("annotations.ExtractInterface");
  }
  public Collection<String> supportedOptions() {
    return Collections.emptySet();
  }
}


一. 要使用apt的时候, 必须指明一个工厂类,或者指明能找到apt 所需的工厂类的路径. 否则 apt 将会自行探索...

二. 使用apt 生成注解处理器时, 无法利用Java 的反射机制,因为apt 操作的是源代码,而不是编译后的类.

AnnotationProcessorFactory 接口只有三个方法, getProcessorFor() 方法返回注解处理器,另外两个方法是supportedAnnotationType () 和 supportedOptions().

supportedAnnotationType() 方法尤其重要, 因为一旦在返回的String 集合中没有你的注解的完整类名, apt 就会因为没有找到相应的处理器, 发出警告信息,然后什么也不做就退出。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: