前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Groovy】编译时元编程 ( 编译时方法注入 | 使用 buildFromSpec、buildFromString、buildFromCode 进行方法注入 )

【Groovy】编译时元编程 ( 编译时方法注入 | 使用 buildFromSpec、buildFromString、buildFromCode 进行方法注入 )

作者头像
韩曙亮
发布2023-03-30 11:08:47
3210
发布2023-03-30 11:08:47
举报
文章被收录于专栏:韩曙亮的移动开发专栏

文章目录

一、在 MyASTTransformation#visit 方法中进行方法注入


【Groovy】编译时元编程 ( 编译时方法拦截 | 在 MyASTTransformation#visit 方法中进行方法拦截 ) 博客中的 方法拦截的基础上进行方法注入 ;

首先对 MethodNode 进行处理

代码语言:javascript
复制
            // 找到了 Student 下的 hello 方法
            // 在 MethodNode 节点下调用
            // it 就是 MethodNode 节点
            BlockStatement blockStatement = code

            // 清空 BlockStatement 中的 List<Statement> statements 成员
            // 方法拦截清空 , 就不再执行原本的方法
            // 方法注入不清空 , 会执行原来的方法内容
            blockStatement.statements.clear()

如果将 blockStatement.statements 清理了 , 就不会再执行 Student#hello 原本的方法内容 ;

保留 blockStatement.statements 原来的集合元素 , 继续向其中添加其它元素 , 可以在原方法的基础上执行其它内容 ;

1、使用 new AstBuilder().buildFromSpec 进行方法注入

先创建方法节点 ,

代码语言:javascript
复制
            // 创建方法节点
            def methods = new AstBuilder().buildFromSpec {
                expression {
                    methodCall {
                        variable('this')
                        constant('println')
                        argumentList {
                            constant('hello buildFromSpec')
                        }
                    }
                }
            }

然后将方法节点 , 添加到 blockStatement.statements 集合中 ;

代码语言:javascript
复制
            // 将方法节点添加到 hello 方法中
            blockStatement.statements.addAll(methods)

2、使用 new AstBuilder().buildFromString 进行方法注入

代码语言:javascript
复制
            // 创建方法节点
            def methods2 = new AstBuilder().buildFromString('println "hello buildFromString"')
            // 将方法节点添加到 hello 方法中
            blockStatement.statements.addAll(methods2)

3、使用 new AstBuilder().buildFromCode 进行方法注入

代码语言:javascript
复制
            // 创建方法节点, 注意此处拿到的是
            def methods3 = new AstBuilder().buildFromCode {
                println "hello buildFromCode"
            }
            // 将方法节点添加到 hello 方法中
            blockStatement.statements.addAll(methods3[0].statements)

二、完整代码示例及进行编译时处理的编译过程


1、Groovy 脚本 Groovy.groovy

代码语言:javascript
复制
class Student{
    def name
    def hello(){
        println "hello"
    }
}

def student = new Student()
student.hello()

2、ASTTransformation 接口实现 MyASTTransformation.groovy

代码语言:javascript
复制
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) {
        println nodes
        println source
        println source.AST
        println source.source.reader.text

        // 获取 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、配置 ASTTransformation

创建 D:\002_Project\012_Groovy\Groovy_Demo\src\main\groovy\resources\META-INF\servicesorg.codehaus.groovy.transform.ASTTransformation 目录层级及文件 , 在文件中配置 ASTTransformation 实现类的全类名 :

代码语言:javascript
复制
MyASTTransformation

3、使用命令行进行编译时处理

首先 , 进入 D:\002_Project\012_Groovy\Groovy_Demo\src\main\groovy 目录 ,

代码语言:javascript
复制
cd D:\002_Project\012_Groovy\Groovy_Demo\src\main\groovy

然后 , 编译 编译时处理类 MyASTTransformation.groovy , 将编译后的字节码文件 MyASTTransformation.class 保存到 D:\002_Project\012_Groovy\Groovy_Demo\src\main\groovy\classes 目录下 ,

代码语言:javascript
复制
groovyc -d classes MyASTTransformation.groovy

再后 , 打包上述编译好的字节码文件 , 存放在 D:\002_Project\012_Groovy\Groovy_Demo\src\main\groovy\test.jar 路径 ;

代码语言:javascript
复制
jar -cf test.jar -C classes . -C resources .

最后 , 依赖 test.jar 执行 Groovy.groovy 脚本

代码语言:javascript
复制
groovy -classpath test.jar Groovy.groovy

执行结果为 :

代码语言:javascript
复制
[org.codehaus.groovy.ast.ModuleNode@7d7758be]
org.codehaus.groovy.control.SourceUnit@2bdd8394
org.codehaus.groovy.ast.ModuleNode@7d7758be
class Student{
    def name
    def hello(){
        println "hello"
    }
}

def student = new Student()
student.hello()
[org.codehaus.groovy.ast.ModuleNode@16ce702d]
org.codehaus.groovy.control.SourceUnit@7b94089b
org.codehaus.groovy.ast.ModuleNode@16ce702d
println "hello buildFromString"
[org.codehaus.groovy.ast.ModuleNode@72c28d64]
org.codehaus.groovy.control.SourceUnit@6492fab5
org.codehaus.groovy.ast.ModuleNode@72c28d64
__synthesized__label__1644323893072__:{
                println "hello buildFromCode"
            }
hello buildFromCode
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-02-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 一、在 MyASTTransformation#visit 方法中进行方法注入
    • 1、使用 new AstBuilder().buildFromSpec 进行方法注入
      • 2、使用 new AstBuilder().buildFromString 进行方法注入
        • 3、使用 new AstBuilder().buildFromCode 进行方法注入
        • 二、完整代码示例及进行编译时处理的编译过程
          • 1、Groovy 脚本 Groovy.groovy
            • 2、ASTTransformation 接口实现 MyASTTransformation.groovy
              • 3、配置 ASTTransformation
                • 3、使用命令行进行编译时处理
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档