一、利用注解进行 AST 语法树转换
1、定义注解并使用 GroovyASTTransformationClass 注明 AST 转换接口
首先 , 定义 Compile 注解 , 该注解名称是任意字符串 , @Target(ElementType.METHOD) 表示该注解作用于方法上 , @GroovyASTTransformationClass("MyASTTransformation") 表示该注解修饰的节点对应的 AST 转换接口实现类是 MyASTTransformation ;
import org.codehaus.groovy.transform.GroovyASTTransformationClass import java.lang.annotation.ElementType import java.lang.annotation.Target /** * 该注解作用于方法上 */ @Target(ElementType.METHOD) @GroovyASTTransformationClass("MyASTTransformation") @interface Compile { }
2、AST 转换接口实现
然后 , 实现 MyASTTransformation 类 , 该类继承 ASTTransformation 接口 ;
import org.codehaus.groovy.ast.ASTNode import org.codehaus.groovy.ast.builder.AstBuilder import org.codehaus.groovy.ast.stmt.BlockStatement import org.codehaus.groovy.control.SourceUnit import org.codehaus.groovy.transform.ASTTransformation import org.codehaus.groovy.transform.GroovyASTTransformation @GroovyASTTransformation class MyASTTransformation implements ASTTransformation { /** * 编译时处理方法 * @param nodes AST 抽象语法树节点 , 是 ASTNode 数组类型 * @param source 源单元 , 可以通过该对象拿到源文件 */ @Override void visit(ASTNode[] nodes, SourceUnit source) { // 获取 Groovy.groovy 脚本中的类集合 , 并进行遍历 // 在 ModuleNode 中的类节点封装在了如下成员中 // List<ClassNode> classes = new LinkedList<ClassNode>(); source.AST.classes.find { // 查找名称为 Student 的类 // it 是 ClassNode 节点 it.name == "Student" }?.methods?.find { // 查找 Student 类下名称为 hello 的方法 // it 是 MethodNode 节点 it.name == "hello" }?.with { // 找到了 Student 下的 hello 方法 // 在 MethodNode 节点下调用 // it 就是 MethodNode 节点 BlockStatement blockStatement = code // 清空 BlockStatement 中的 List<Statement> statements 成员 // 方法拦截清空 , 就不再执行原本的方法 // 方法注入不清空 , 会执行原来的方法内容 //blockStatement.statements.clear() // 创建方法节点 def methods = new AstBuilder().buildFromSpec { expression { methodCall { variable('this') constant('println') argumentList { constant('hello buildFromSpec') } } } } // 将方法节点添加到 hello 方法中 //blockStatement.statements.addAll(methods) // 创建方法节点 def methods2 = new AstBuilder().buildFromString('println "hello buildFromString"') // 将方法节点添加到 hello 方法中 //blockStatement.statements.addAll(methods2) // 创建方法节点, 注意此处拿到的是 def methods3 = new AstBuilder().buildFromCode { println "hello buildFromCode" } // 将方法节点添加到 hello 方法中 blockStatement.statements.addAll(methods3[0].statements) } } }
3、定义 Groovy 类并使用 @Compile 注解修饰需要拦截的方法
最后 , 实现 Groovy 类 , 在该类的方法上使用 @Compile 注解 ;
class Student{ def name @Compile def hello(){ println "hello" } } def student = new Student() student.hello()
4、执行结果
执行上述 Groovy 脚本 , 执行结果为 :
hello hello buildFromCode