利用ASTTransformation 来实现
lsn11_0.groovy
class Content{
def a;
def b(){
}
}
println()
MyASTTansformation.groovy
import org.codehaus.groovy.ast.ASTNode
import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.ast.ConstructorNode
import org.codehaus.groovy.ast.FieldNode
import org.codehaus.groovy.ast.GroovyClassVisitor
import org.codehaus.groovy.ast.MethodNode
import org.codehaus.groovy.ast.PropertyNode
import org.codehaus.groovy.control.SourceUnit
import org.codehaus.groovy.transform.ASTTransformation
import org.codehaus.groovy.transform.GroovyASTTransformation
// 如果在运行编译将我们的MyASTTansformation 添加到classPath里面
// 就能够在 这个自定的的实现了这个接口的类当中visit方法里面来
// 获得所有编译的节点,所有编译的源文件代码,还有类的方法 都可以获得
@GroovyASTTransformation
class MyASTTansformation implements ASTTransformation{
/**
*
* @param nodes :节点 ast抽象语法数节点(如果安装好了Groovy环境变量可以,
* 在命令行当中来输入groovyConsole,在这个工具当中,然后将 Content这个类放到这个
* 工具中,这个工具能帮我们分析ast,点击Script 的 Inspect Ast,这就是ast语法树,有
* Methods,Fields,Properties拿到这个方法树,就意味着我们拿到这这个放到每一个节点,
* 能拿到b方法的实现,能拿到a属性的值,都能获得)
* @param source :源单元 (能拿到源文件)
*/
@Override
void visit(ASTNode[] nodes, SourceUnit source) {
// 通过source 拿到源文件
// 执行MyASTTansformation 必须创建META-INF
// 为了方便,用命令行的方式
// 先在groovy目录下创建resources目录
// 然后创建META-INF.services目录
// 然后在这个目录下创建一个org.codehaus.groovy.transform.ASTTransformation文件
// 在这个文件当中写入 ASTTransformation 的实现类的全类名
// 1.然后通过命令行groovyc -d classes MyASTTansformation.groovy 进行编译
// 2. 然后打包 jar -cf test.jar -C classes . -C resources .
// (然后就会在groovy目录下有一个test.jar)
//3. 然后执行 groovy -classpath test.jar lsn11_0.groovy
// 将刚才打出来的包 加到classpath当中来执行我们的
println nodes // [org.codehaus.groovy.ast.ModuleNode@61009542]
// (ModuleNode就是lsn11_0.groovy的节点)
// Groovy源文件可以创建很多 class,所以ModuleNode类里面
// 有一个List ,还有 List 方法
// List starImports = new ArrayList();
// Map staticImports = new LinkedHashMap();
// 就是 import节点
println source // org.codehaus.groovy.control.SourceUnit@77e9807f
println source.AST // org.codehaus.groovy.ast.ModuleNode@61009542
source.AST.classes.each {
//println it.name // Content 可以拿到类名
it.visitContents(new GroovyClassVisitor() { // 分析访问 我们的 类
@Override
void visitClass(ClassNode node) { // 分析类
}
@Override
void visitConstructor(ConstructorNode node) { // 分析构造方法
}
@Override
void visitMethod(MethodNode node) { // 分析方法
if (node.name.length() == 1){
println "$node.name too short" // b too short
}
}
@Override
void visitField(FieldNode node) { // 分析 属性
if (node.name.length() == 1){
println "$node.name too short" // a too short
}
}
@Override
void visitProperty(PropertyNode node) {
}
})
}
println source.source.reader.text
// class Content{
// def a;
// def b(){
//
// }
// }
//
// println()
}
}
org.codehaus.groovy.transform.ASTTransformation
MyASTTansformation
如果希望在编译时候,来拦截一个方法,或者在这个方法当中注入代码的话
也可以通过ASTTransformation 来实现
先创建一个新的模块 叫 inject, 在inject当中来拦截一个方法
source.groovy
// 希望通过Transformation 来修改 soutMsg的实现
class Content {
def soutMsg(){
println('ms')
}
}
new Content().soutMsg()
InjectAST.groovy
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 InjectAST implements ASTTransformation {
@Override
void visit(ASTNode[] nodes, SourceUnit source) {
source.AST.classes.find{
// 找到Content类
it.name == "Content"
}?.methods?.find{
// 找到soutMsg方法
it.name == "soutMsg"
}?.with {
// 通过groovyConsole分析节点
// 发现 code其实是个BlockStatement
// 获得methodnode中的代码块实现
BlockStatement block = code
// BlockStatement 有一个 ExpressionStatement
// block.getStatements()
// public List getStatements() 这个集合就是每一个实现节点
// ==== 1 . 方法的拦截 ====
// 替换实现,先把这个方法节点里面所有的节点清空
// 清空原始的实现
// block.statements.clear()
// 然后添加自己的实现
// 创建自定义实现
// 有三种创建,buildFromSpec,buildFromCode,buildFromString
// 1.buildFromSpec
def methods = new AstBuilder().buildFromSpec {
//dsl 配合 groovyConsole使用
expression{
methodCall {
variable('this')
constant('println')
argumentList {
constant('replace')
}
}
}
}
block.statements.addAll(methods)
// println methods // ExpressionStatement
// 2.buildFromString
// block.statements.clear()
// methods = new AstBuilder().buildFromString("""println '123456'""")
// println methods // BlockStatement
// block.statements.addAll(methods[0].statements)
// 3.buildFromCode
// block.statements.clear()
methods = new AstBuilder().buildFromCode {
println 'zeking'
}
// println methods // BlockStatement
// block.statements.addAll(methods[0].statements) // 添加到后面 ,,
// ms
// replace
// zeking
// ==== 2 . 方法的注入 ====
block.statements.add(0,methods[0].statements[0]) // 添加到前面
// zeking
}
}
// groovyc -d classes InjectAST.groovy
// jar -cf inject.jar -C classes . -C resources .
// groovy -classpath inject.jar source.groovy
// 打印了 replace
}
org.codehaus.groovy.transform.ASTTransformation
InjectAST
groovyc -d classes InjectAST.groovy
jar -cf inject.jar -C classes . -C resources .
groovy -classpath inject.jar source.groovy
打印 replace
基本上就跟apt一样了
创建新的module : annotationAST
UseAnno.groovy
class Content{
@Check
def fun(){
Thread.sleep(2_000)
}
}
new Content().fun()
需求: 将上面fun的实现变为
def fun(){
def start = System.nanoTime()
Thread.sleep(2_000)
def use = System.nanoTime()-start
println(use)
}
Check.groovy
import org.codehaus.groovy.transform.GroovyASTTransformation
import org.codehaus.groovy.transform.GroovyASTTransformationClass
import java.lang.annotation.ElementType
import java.lang.annotation.Target
@Target(ElementType.METHOD)
@GroovyASTTransformationClass('AptAst') // 这样就不用创建MEF-INF.services 这个资源了
@interface Check {
}
AptAst.groovy
mport org.codehaus.groovy.ast.ASTNode
import org.codehaus.groovy.ast.MethodNode
import org.codehaus.groovy.ast.builder.AstBuilder
import org.codehaus.groovy.ast.stmt.BlockStatement
import org.codehaus.groovy.ast.stmt.ExpressionStatement
import org.codehaus.groovy.ast.stmt.ReturnStatement
import org.codehaus.groovy.control.SourceUnit
import org.codehaus.groovy.transform.ASTTransformation
import org.codehaus.groovy.transform.GroovyASTTransformation
@GroovyASTTransformation
class AptAst implements ASTTransformation {
@Override
void visit(ASTNode[] nodes, SourceUnit source) {
// source.AST.find{
// it.name == 'Content'
// }
// println nodes
// [org.codehaus.groovy.ast.AnnotationNode@64d2d351, MethodNode@107456312[java.lang.Object fun()]]
// AnnotationNode 就是使用的注解,而MethodNode 就是被我们注解声明的方法 也就是fun
// 所以可以直接在nodes上面来拿MethodNode
// nodes 就是拿到了使用了@Check 这个注解的 对应的 注解节点与 方法节点
def methodnodes = nodes.findAll { it instanceof MethodNode }
methodnodes.each {
MethodNode node ->
def startStatement = new AstBuilder().buildFromCode {
def start = System.nanoTime()
}
def endStatement = new AstBuilder().buildFromCode {
def use = System.nanoTime() - start
println("use:${use/1.0e9}")
}
// println startStatement
//[org.codehaus.groovy.ast.stmt.BlockStatement@2e377400[org.codehaus.groovy.ast.stmt.ReturnStatement
// 发现 startStatement 是个BlockStatement 并且 ReturnStatement
// return 了 后面的内容就不会执行了,也就是endStatement 不会执行了
// println endStatement
BlockStatement blockStatement = node.code
ReturnStatement returnStatement = startStatement[0].statements[0]
blockStatement.statements.add(0, new ExpressionStatement(returnStatement.expression))
blockStatement.statements.addAll(endStatement[0].statements)
}
}
}
groovyc -d classes Check.groovy AptAst.groovy
jar -cf annotation.jar -C classes .
groovy -classpath annotation.jar UseAnno.groovy
输出 use:2.003856146