Annotation Processor
Processor处理约定
- JavaC编译环境获取当前的源文件及类文件, 建立多轮次的处理过程。每一次轮次的处理结果将作为下一轮的输入。当某一轮处理完成后没有产生新的源文件或类文件,触发最后一轮。
- Processors 通过getSupportedAnnotationTypes、 getSupportedOptions、 getSupportedSourceVersion 方法确定是否匹配需要处理的Annotation.
- Processor的process方法返回值: false表明匹配注解没有被processor正确处理, 对应的注解将继续寻找合适的processor处理, 返回true表明已经被处理,对应的注解在本轮次将不再交给别的processor处理。 Process方法如果抛出未捕获的异常,将中断编译过程。
Processor 接口方法
/** *识别和匹配注解的@SupportedOptions配置 */ Set<String> getSupportedOptions(); /** *返回processor匹配的注解类型 */ Set<String> getSupportedAnnotationTypes(); /** *返回process支持的最新源码版本 */ SourceVersion getSupportedSourceVersion(); /** *由执行环境调用对processor进行初始化 */ void init(ProcessingEnvironment processingEnv); /** *注解处理方法 */ boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv); |
抽象语法树(AST)
Annotation Processor目前所常见的场景就是编译时根据特定注解改造最终生成的类文件, 如lombok。 相应功能离不开JAVA 抽象语法树(AST)的概念及相关接口。AST 是JAVA语法的抽象, 编译过程中源文件的Java语法将解析为AST并提供相应操作接口进行读取和修改。
com.sun.source.tree.Tree接口: 语法树的基础接口, 所有的语法块均实现该接口,提供访问者模式接口:<R, D> R accept(TreeVisitor<R, D> var1, D var2);
com.sun.source.util.Trees类: 语法树工具类, 提供了获取语法树相关Tree对象的方法。使用的实现类JavacTrees
Tree.Kind 枚举: 定义了语法块的类型。如 ANNOTATION(AnnotationTree.class),
JCTree: Tree接口的抽象实现类, 其他具体类型集成实现该类。
TreeTranslator:JCTree的访问者接口实现。 Processor实现中实现该类的子类,覆盖注解目标对应类型的visit方法,如访问类结构的visitClassDef(JCClassDecl jcClassDecl)方法。
Processor示例-样例
考虑一个问题: 如何往一个不能直接修改源码的类里面增加域? 比如接口协议对象作为一个规范,不允许进行源码修改特定类型。但要进行扩展,增加参数。
当然答案有很多,比如 通过替换同名的类, 通过Spring的FactoryBean等扩展点替换掉类定义等。这里说明如何通过Annotation processor的方式实现。
需要做的工作包括:
- 定义一个注解,该注解作用在类上, 作用范围是源码。
- 在使用该注解的类上增加需要添加的域, 并且通过某种方式指明要讲新的域加入的目标类型。 这里有不同的实现方式,比如可以再注解中直接指明目标类型,示例使用集成的方式说明新的域要加入到父类中,这样保持类型语义的连续性。
- 实现Annotation Processor, 对新增了注解的类进行解析,将新增注解类中的域添加到父类中(重新父类的类文件)。
关键代码如下:
@Retention(RetentionPolicy.SOURCE) @Target({ElementType.TYPE}) public @interface InsertField { } |
public synchronized void init(ProcessingEnvironment processingEnv) { //messager用于打印日记 this.messager = processingEnv.getMessager(); //获取Trees对象,作为Tree语法块的获取入口 this.trees = JavacTrees.instance(processingEnv); } |
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { try { this.messager.printMessage(Kind.NOTE, "start to process extend POJO"); //获取打了InsertField注解的类 Iterator insertFieldClassIterator = roundEnv.getElementsAnnotatedWith(InsertField.class).iterator(); while(insertFieldClassIterator.hasNext()) { Element element = (Element)insertFieldClassIterator.next(); JCTree jcTree = this.trees.getTree(element); jcTree.accept(new TreeTranslator() { public void visitClassDef(JCClassDecl jcClassDecl) { try { ClassPool cp = ClassPool.getDefault(); //获取其继承的父类,需要将域插入到父类中 String targetClassName = ((JCIdent)jcClassDecl.extending).sym.toString(); targetClassName = StringUtils.replace(targetClassName, ".", "/")+ ".class"; //读入要修改的类 InputStream targetClassStream = this.getClass().getClassLoader().getResourceAsStream(targetClassName); if (targetClassStream == null) { this.messager.printMessage(Kind.WARNING, "非标准类, 无法插入"); } else { //生成CtClass对象 用于插入新增的域及方法 CtClass targetClass = cp.makeClass(targetClassStream); //遍历注解类的定义,获取域 List<JCVariableDecl> jcVariableDeclList = List.nil(); Iterator defsIterator = jcClassDecl.defs.iterator(); while(defsIterator.hasNext()) { JCTree tree = (JCTree)defsIterator.next(); if (tree.getKind().equals(com.sun.source.tree.Tree.Kind.VARIABLE)) { JCVariableDecl jcVariableDecl = (JCVariableDecl)tree; Set<Modifier> flags = jcVariableDecl.mods.getFlags(); if (!flags.contains(Modifier.FINAL) && !flags.contains(Modifier.STATIC)) { jcVariableDeclList = jcVariableDeclList.append(jcVariableDecl); } } } var7 = jcVariableDeclList.iterator(); while(var7.hasNext()) { JCVariableDecl jcVariableDeclx = (JCVariableDecl)var7.next(); ExtendPOJOProcessor.this.messager.printMessage(Kind.NOTE, jcVariableDeclx.getName() + " has been processed"); String fieldType = jcVariableDeclx.vartype.type.tsym.toString(); String fieldName = (new StringBuffer()).append("private ").append(fieldType).append(" ").append(jcVariableDeclx.getName()).append(";").toString(); if (cp.getOrNull(fieldType) == null) { cp.makeClass(fieldType); } //创建新增的域 CtField ctField = CtField.make(fieldName, targetClass); ConstPool constPool = targetClass.getClassFile().getConstPool(); String recapitalizedName = org.springframework.util.StringUtils.capitalize(ctField.getName()); String setterMethodName = "set" + recapitalizedName; String getterMethodName = "get" + recapitalizedName; //创建新增域的get/set方法 CtMethod fieldSetter = CtNewMethod.setter(setterMethodName, ctField); CtMethod fieldGetter = CtNewMethod.getter(getterMethodName, ctField); //设置方法的signature List<Type> types = jcVariableDeclx.vartype.type.getTypeArguments(); if (CollectionUtils.isNotEmpty(types)) { StringBuilder signatureBuilder = (new StringBuilder("L")).append(Descriptor.toJvmName(jcVariableDeclx.vartype.type.tsym.toString())).append("<"); Iterator typeIterator = types.iterator(); while(typeIterator.hasNext()) { Type type = (Type)typeIterator.next(); signatureBuilder.append(Descriptor.of(type.toString())); } signatureBuilder.append(">;"); ctField.setGenericSignature(signatureBuilder.toString()); fieldSetter.setGenericSignature("(" + signatureBuilder + ")V"); fieldGetter.setGenericSignature("()" + signatureBuilder); } //将域加入targetClass targetClass.addField(ctField); //将域的getter方法加入targetClass targetClass.addMethod(fieldSetter); //将域的setter方法加入targetClass targetClass.addMethod(fieldGetter); } //重写要插入域的类文件 String classPath = this.getClass().getResource("/").getPath(); targetClass.writeFile(classPath); targetClass.defrost(); super.visitClassDef(jcClassDecl); } } catch (Throwable ex1) { throw ex1; } } }); } return true; } catch (Throwable ex2) { throw ex2; } } |
在resources/META-INF/services路径下新建javax.annotation.processing.Processor文件,写入Processor实现类:
com.demo.annotationprocess.InsertFieldProcessor