Annotation Processing Tool(APT)

参考:

  • Thinking in Java注解一章

  在Java中有一个apt的命令行工具(也包含了javac命令的功能,即编译功能),它的作用是在编译源文件(*.java)之前,通过一个自定义的注解处理器(AnnotationProcessor)解释并处理源文件中的注解,由注解处理器生成一些新的源文件,字节码文件(*.class),或其他的文本文件,APT也会对新生成源文件进行编译,直到没有新的文件生成。

 

APT适用范围 :当某些类的生成需要依赖一些元数据的时候 还真不知道在什么时候需要依赖元数据


APT的作用 (基本上翻译自官网)

  1. 由于类的生成是通过注解元数据生成,而不是通过手工编写,所以类的维护对象变成是带有注解元数据的类(官方称为带有声明的模型:the model of declarations)。
  2. 有一个清晰的声明模型和应用结构。
  3. 可以使用更一般的类型,如用Collection代替Array。另外可以通过观察者模式操作各种声明和数据类型。
  4. 它可以递归地处理和编译新生成的源文件,字节码文件,或其他文件。

 

需要用到的相关包和接口:

  • com.sun.mirror.apt :与APT工具交互的一些 接口AnnotationProcessor,AnnotationProcessorFactory ,AnnotationProcessorEnvironment等。
  • com.sun.mirror.declaration: 映射到源代码的classes,fields,methods等的声明。实际上跟Java的反射机制中的Class,Field,Method有点类似,区别在于前者对应的是源代码,后者对应的是编译后运行时的类、属性域、方法等。
  • com.sun.mirror.type :映射到源代码的数据类型。如:AnnotationType对应的是注解的类型,PrimitiveType对应的是基本数据类型,ect.
  • com.sun.mirror.util :处理declaration和type的一些工具类,像观察者(Vistor)接口,Declarations工具接口ect.

  AnnotationProcessorFactory :注解处理器工厂,它主要负责与APT工具交互,并提供注解处理器(AnnotationProcessor),如果需要提供多个注解处理器,可以使用 com.sun.mirror.apt.AnnotationProcessors.getCompositeAnnotationProcessor捆绑多个注解处理器

  AnnotationProcessor :注解处理器,它实现一个process()方法,所有对注解的解释和处理都在这里进行。

  AnnotationProcessorEnvironment :注解处理环境,它封装了 整个 注解处理过程所需要的方法、数据,如getSpecifiedTypeDeclarations()返回所有需要处理的源类(TypeDeclaration),注解处理环境由APT工具生成并传递给 AnnotationProcessorFactory。

Note:这些类包、接口可以在JDK安装目录下的lib/tools.jar中找到,因此需要在系统环境变量的CLASSPATH中添加!

 

APT应用流程:

  1. 编写需要进行注解处理的类(一般地,这些类应该带有需要处理的注解)。
  2. 实现至少一个AnnotationProcessorFactory。
  3. 实现AnnotationProcessor
  4. 使用APT命令行,执行注解处理。

样例(摘自官网):

import com.sun.mirror.apt.*;
import com.sun.mirror.declaration.*;
import com.sun.mirror.type.*;
import com.sun.mirror.util.*;

import java.util.Collection;
import java.util.Set;
import java.util.Arrays;

import static java.util.Collections.*;
import static com.sun.mirror.util.DeclarationVisitors.*;

/*
 * This class is used to run an annotation processor that lists class
 * names.  The functionality of the processor is analogous to the
 * ListClass doclet in the Doclet Overview.
 * 这是注解处理工厂
 */
public class ListClassApf implements AnnotationProcessorFactory {
   
    private static final Collection<String> supportedAnnotations
        = unmodifiableCollection(Arrays.asList("*"));

    
    private static final Collection<String> supportedOptions = emptySet();
  
  /* 
    这里返回的是该AnnotationProcessorFactory可以处理的注解类名的字符串集合。注解类名可以有以下三种类名形式:
 1. *代表支持所有注解类
 2. com.sin90.*代表支持com.sin90包内所有的注解类
   3. com.sin90.ISchool代表支持特指的com.sin90.ISchool注解类
   样例中的当前值为可以处理所有注解类
   */
    public Collection<String> supportedAnnotationTypes() {
        return supportedAnnotations;
    }

 /*
        这里返回的是该工厂可以支持接收APT工具的参数名的集合。这里对应的是apt命令的-A选项,如:想接收命令行"-Alog=3 -At=tim",那么该方法应该返回{"-Alog","-At"}
        样例中为不接收任何参数。   
    */
    public Collection<String> supportedOptions() {
        return supportedOptions;
    }

    /*
       返回AnnotationProcessor的接口方法,其中
       atds:为APT所扫描的源文件中出现过的所有注解。(因为源代码中的注解可以用AnnotationTypeDeclaration表示)
       env:APT工具传递进来的注解处理环境。
    */
    public AnnotationProcessor getProcessorFor(
            Set<AnnotationTypeDeclaration> atds,
            AnnotationProcessorEnvironment env) {
        return new ListClassAp(env);
    }

    // 这是注解处理器
    private static class ListClassAp implements AnnotationProcessor {
        private final AnnotationProcessorEnvironment env;
        ListClassAp(AnnotationProcessorEnvironment env) {
            this.env = env;
        }

        //注解处理....
        public void process() {
	    for (TypeDeclaration typeDecl : env.getSpecifiedTypeDeclarations())
     //使用观察者模式进行注解处理
     //com.sun.mirror.util.DeclarationVisitors.getDeclarationScanner()方法的两个参数都是观察者实现类,前者是在注解处理时使用,后者是在注解处理后使用
		typeDecl.accept(getDeclarationScanner(new ListClassVisitor(),
						      NO_OP));
        }


   //观察者实现类
	private static class ListClassVisitor extends SimpleDeclarationVisitor {
	    public void visitClassDeclaration(ClassDeclaration d) {
		System.out.println(d.getQualifiedName());
	    }
	}
    }
}
 

 

APT命令行

语法:apt [-classpath classpath ] [-sourcepath sourcepath ] [-d directory ] [-s directory ] [-factorypath path ] [-factory class ] [-print] [-nocompile] [-Akey [=val ] ...] [javac option ] [sourcefiles] [@files]

Note:命令行中的*path值指的都是相对于当前 系统的 工作目录而言的,而class/classname指的是完整的类名,如classpath指的是定义类库的引用目录,它的值要于windows的路径写法一致,而-factory的class参数写的是完整的类名com.sin90.apt.NewAnnotationProcessorFactory。具体请看官方的APT命令介绍

 

假设在d:\sourcefiles文件夹中包含有com.sin90.apt.NewAnnotationProcessorFactory,com.sin90.apt.NewAnnotationProcessor,com.sin90.New(需要注解处理的源文件)

 

那么应用APT工具的顺序:

  1. 编译AnnotationProcessorFactory。d:\sourcefiles>javac com\sin90\apt\NewAnnotationProcessorFactory.java
  2. 调用apt。d:\sourcefiles> apt -factory com.sin90.apt.NewAnnotationProcessor com\sin90\New.java

 

备注:关于APT也是在看Thinking in java的时候看到的,因为不太懂,便上官网查看文档,并把学到的东西记在该文中,现在虽然大概知道APT的作用,但还有一个很大的疑问:如何在程序运行期间调用APT命令行呢?那么在程序运行期间生成的新类能在程序中调用吗?如果不能在运行期间调用APT工具生成的新类,那么APT就没什么作用了吧?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值