EJB3笔记7-EJB3 Framwork:apt解析annotation生成代码

fancyh 2005-06-01 09:29:10
EJB3笔记7-EJB3 Framwork:apt解析annotation生成代码
Author fancyhf@163.com fancyhf.mblogger.cn

Apt处理流程:
1.apt确定源代码有那些annotations
2.apt查找你写的annotation processor factories,询问factories要处理哪里annotation。
3.processor运行生成新代码文件直到没有新的文件要被产生。

有关的包:
com.sun.mirror.apt:跟apt工具交互的接口
com.sun.mirror.declaration:建模源代码的字段、方法、类等的定义
com.sun.mirror.type:建模在源代码中找到的类型
com.sun.mirror.util:处理type,declaration,visitor的功能集

每个processor实现com.sun.mirror.apt包的AnnotationProcessor接口,这个接口包括一个process方法,将被apt工具调用。一个processor将处理一或多个annotation types。

通过AnnotationProcessorFactory获得一个processor实例,工具将提供AnnotationProcessorEnvironment。在这个环境变量中,process将找到任何启动时需要的值,包括其操作的程序接口,跟apt工具交互操作产生新文件和传送警告和错误信息的方式。

可通过命令行的-facotry来指定factory,也可从META-INF/services读取名为com.sun.mirror.apt.AnnotationProcessorFactory的文本文件,文件列出有关的facotry类。例子:

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;

//以下是static import性能
import static java.util.Collections.*;
import static com.sun.mirror.util.DeclarationVisitors.*;


/*列出类名,功能类似doclet的ListClass。*/
public class ListClassApf implements AnnotationProcessorFactory {
//处理任何annotations
private static final Collection<String> supportedAnnotations
= unmodifiableCollection(Arrays.asList("*"));//或可改变*为Command标记

// No supported options
private static final Collection<String> supportedOptions = emptySet();

public Collection<String> supportedAnnotationTypes() {
return supportedAnnotations;
}

public Collection<String> supportedOptions() {
return supportedOptions;
}

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())
typeDecl.accept(getDeclarationScanner(new ListClassVisitor(),
NO_OP));
}

private static class ListClassVisitor extends SimpleDeclarationVisitor {
public void visitClassDeclaration(ClassDeclaration d) {
System.out.println(d.getQualifiedName());
}
}
}
}

指定处理的annotations:
factory返回import-style字符的结合,如例子。特定的字符入口包括以下格式:
*:处理所有的annotations。也处理空的annotation列表,也即处理就必须提供non-trivial即使没有annotation。这个功能使得com.sun.mirror的api能用来写通用代码生成工具。
foo.bar.Baz:处理规范名为“ foo.bar.Baz”的annotation。
foo.bar.*:处理规范名由“foo.bar.”开始的annotation。
====一定要包括包名,而且不支持Command*,所以不同的annotation可能需要放不不同的subpackage


facotry可使用com.sun.mirror.apt.AnnotationProcessors.getCompositeAnnotationProcessor来联合切按序操作多个annotation processor。

可通过-A传入参数给processor,如-Adebug和-Aloglevel=3。

隐形参数:
-XListAnnotationTypes :List found annotation types
-XListDeclarations :List specified and included declarations
-XPrintAptRounds :Print information about initial and recursive apt rounds
-XPrintFactoryInfo :Print information about which annotations a factory is asked to process

声明和类型:
mirror api主要通过com.sun.mirror.declaration包中的Declaration接口和下层子接口来表示源代码结构。一个Declaration表示一个程序结构如包、类或方法,通常一对一关联到源代码中特定的片断。Declaration是要被标注的结构。

Type通过com.sun.mirror.type包中的TypeMirror接口和其下层子接口表述。包括主类行、类、接口类行、数组类行、类型变量和统配符类型。

Declaration和type有所不同,一个单一的declaration可以定义一个完整的type家族,如类java.util.Set的declaration关联到:
参数类行 java.util.Set<String>
参数类行java.util.Set<Number>
参数类行java.util.Set<T> for some type T other than String or Number the raw type java.util.Set.

一个declaration有doc被主,源代码位置,modifier和标注,它可有不同类型的名称(简单、合格)。更多特定的declaration子类提供访问构造函数和父类。

TypeMirrors用来建模源代码中的返回类型、参数类型等,一个蚕注类型的TypeMirror映射到有关的declaration。

Apt与javadoc的不同:
Apt生成文件,源代码可有标注了annotation,也可以没有标注;之后直接compile所有代码。

如果任何processor生成了新的源代码,将递归调用apt。在这个递归apt调用中,调用上个循环中的facotry的getProcessorFor提供processor,即使那个factory不processor当前任何一个annotation。这允许factory在apt的子循序循环中注册一个listner;尽管多数factories在这个例子中只会简单返回AnnotationProcessors.NO_OP。当一个循环后没有产生新的代码文件,apt将调用javac来编译原始和生成的代码文件。如果没有processors被找到或者不支持当前annotation,调用apt就等于调用javac。

如果一个facotry类被annotation process的多个循环使用,factory导入一次,而其getProcessorFor方法将在每次循环中调用。这使得一个factory能在跨循环保存静态状态。

所以,生成代码前,一定要检测是否属于标注的annotation,如:
boolean isCommandClass = false;
CommandClass ann = d.getAnnotation(CommandClass.class);
isCommandClass = (ann != null);
if (isCommandClass){
PrintWriter writer =
env.getFiler().createSourceFile(packageName+"."+className+"command");
}

...全文
73 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

67,514

社区成员

发帖
与我相关
我的任务
社区描述
J2EE只是Java企业应用。我们需要一个跨J2SE/WEB/EJB的微容器,保护我们的业务核心组件(中间件),以延续它的生命力,而不是依赖J2SE/J2EE版本。
社区管理员
  • Java EE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧