You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Groovy类中加载当前运行方法所在文件的源码为字符串?

获取Groovy类中当前方法所在文件的源码字符串

嘿,这个问题我之前也踩过坑——Groovy脚本确实能直接用script.source拿源码,但类的处理逻辑完全不一样。核心思路是通过类的protectionDomain定位到编译后的class文件,再反向找到对应的Groovy源文件,最后读取文件内容。不过得先说明:这个方案只适用于源码文件和编译后的class路径对应的场景(比如开发环境、或源码和class一起打包的情况),如果是只包含class的Jar包,那肯定拿不到源码。

具体实现代码

给你一个可以直接嵌入类里的工具方法,基础场景下够用:

class MyGroovyService {
    String getCurrentClassSource() {
        // 获取当前类的代码源位置
        def codeSource = this.getClass().protectionDomain.codeSource
        if (!codeSource) {
            throw new IllegalStateException("无法获取类的代码源信息,可能是动态生成类或Jar中未包含源码")
        }
        
        // 将URL转换为文件路径,兼容Windows/Unix系统
        def classFile = new File(codeSource.location.toURI())
        // 替换.class后缀为.groovy,定位到源文件
        def sourceFile = new File(classFile.path.replaceAll(/\.class$/, ".groovy"))
        
        if (!sourceFile.exists()) {
            throw new FileNotFoundException("找不到对应的Groovy源文件: ${sourceFile.path}")
        }
        
        // 指定编码读取,避免乱码
        return sourceFile.getText("UTF-8")
    }

    void businessMethod() {
        // 调用方法获取整个类的源码
        String classSource = getCurrentClassSource()
        println("当前类的源码内容:\n${classSource}")
    }
}

关键注意事项

  • 路径映射调整:如果你的项目用Gradle/Maven构建,class文件会在target/classes或build/classes目录,而源码在src/main/groovy,那上面的代码需要调整路径映射。比如:
    // 适配Maven项目的路径结构
    def classPathParts = classFile.path.split(File.separator + "target" + File.separator)
    if (classPathParts.length == 2) {
        sourceFile = new File("src/main/groovy/${classPathParts[1]}".replaceAll(/\.class$/, ".groovy"))
    }
    
    这个得根据你项目的实际目录结构修改。
  • Jar环境限制:如果类被打包成不含源码的Jar,这个方法会直接失效。这种场景下可以把源码作为资源文件打包到Jar里,然后用this.getClass().getResourceAsStream("/com/your/package/MyGroovyService.groovy")读取。
  • 编码问题:读取文件时一定要指定项目使用的编码(比如UTF-8),不然容易出现乱码。

进阶:定位当前方法的代码片段

如果你不仅要整个文件的源码,还想拿到当前方法的具体代码,可以结合栈信息和行号来截取。这里给个简单的实现(适合方法定义不复杂的场景):

String getCurrentMethodSource() {
    // 获取当前方法的栈追踪信息
    def stackTrace = Thread.currentThread().stackTrace
    def currentMethodEle = stackTrace.find { it.methodName == "getCurrentMethodSource" }
    if (!currentMethodEle) {
        throw new IllegalStateException("无法定位当前方法的栈信息")
    }
    int startLine = currentMethodEle.lineNumber

    // 获取源码的行列表
    def sourceLines = getCurrentClassSource().readLines()

    // 定位方法起始行(简单匹配方法定义的开头)
    int methodStart = sourceLines.findIndexOf(startLine-2) { 
        it.trim().startsWith("def ${currentMethodEle.methodName}") || 
        it.trim().startsWith("${currentMethodEle.methodName}")
    }
    methodStart = methodStart == -1 ? startLine - 1 : methodStart

    // 定位方法结束行(匹配闭合大括号)
    int braceCount = 0
    int methodEnd = sourceLines.findIndexOf(methodStart) { line ->
        if (line.contains("{")) braceCount++
        if (line.contains("}")) braceCount--
        braceCount == 0
    }

    return sourceLines.subList(methodStart, methodEnd+1).join("\n")
}

这个实现对复杂方法(比如多行参数、内部类嵌套)的支持有限,需要的话可以结合Groovy的AST工具做更精准的解析。

内容的提问来源于stack exchange,提问作者Michal Kordas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 08:19:46