参考:
- Thinking in Java注解一章
在Java中有一个apt的命令行工具(也包含了javac命令的功能,即编译功能),它的作用是在编译源文件(*.java)之前,通过一个自定义的注解处理器(AnnotationProcessor)解释并处理源文件中的注解,由注解处理器生成一些新的源文件,字节码文件(*.class),或其他的文本文件,APT也会对新生成源文件进行编译,直到没有新的文件生成。
APT适用范围 :当某些类的生成需要依赖一些元数据的时候 (还真不知道在什么时候需要依赖元数据 )
APT的作用 (基本上翻译自官网)
- 由于类的生成是通过注解元数据生成,而不是通过手工编写,所以类的维护对象变成是带有注解元数据的类(官方称为带有声明的模型:the model of declarations)。
- 有一个清晰的声明模型和应用结构。
- 可以使用更一般的类型,如用Collection代替Array。另外可以通过观察者模式操作各种声明和数据类型。
- 它可以递归地处理和编译新生成的源文件,字节码文件,或其他文件。
需要用到的相关包和接口:
- 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应用流程:
- 编写需要进行注解处理的类(一般地,这些类应该带有需要处理的注解)。
- 实现至少一个AnnotationProcessorFactory。
- 实现AnnotationProcessor
- 使用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工具的顺序:
- 编译AnnotationProcessorFactory。d:\sourcefiles>javac com\sin90\apt\NewAnnotationProcessorFactory.java
- 调用apt。d:\sourcefiles> apt -factory com.sin90.apt.NewAnnotationProcessor com\sin90\New.java
备注:关于APT也是在看Thinking in java的时候看到的,因为不太懂,便上官网查看文档,并把学到的东西记在该文中,现在虽然大概知道APT的作用,但还有一个很大的疑问:如何在程序运行期间调用APT命令行呢?那么在程序运行期间生成的新类能在程序中调用吗?如果不能在运行期间调用APT工具生成的新类,那么APT就没什么作用了吧?