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

Groovy DSL开发:如何抛出异常定位DSL代码错误行?

解决Groovy DSL中的委托异常定位与非预期闭包拦截问题

一、定位DSL错误行并抛出特定类型异常

你的当前代码中,当DSL用户调用了MyType不存在的方法时,默认的MissingMethodException可能不会清晰指向DSL脚本的错误行,或者缺少友好的提示信息。可以通过以下步骤优化:

1. 配置闭包解析策略+自定义异常包装

首先将闭包的resolveStrategy设置为DELEGATE_FIRST,确保优先查找委托对象(MyType)的方法,避免误匹配到Script的方法;然后捕获缺失方法异常,包装成自定义异常并提取DSL脚本的错误行号:

abstract class MyScript extends Script { 
    def type(@DelegateTo(MyType) Closure cl) { 
        def myType = new MyType()
        cl.delegate = myType
        cl.resolveStrategy = Closure.DELEGATE_FIRST // 优先使用委托对象的方法
        try {
            cl.call()
        } catch (MissingMethodException e) {
            wrapAndThrowDSLException(e)
        }
        this 
    }

    private void wrapAndThrowDSLException(MissingMethodException e) {
        // 从栈轨迹中定位DSL脚本的错误行
        def dslErrorLine = e.stackTrace.find { 
            it.className == this.class.name // 匹配DSL脚本生成的类名
        }
        if (dslErrorLine) {
            throw new DSLValidationException(
                "DSL错误:在第${dslErrorLine.lineNumber}行调用了不存在的方法'${e.method}'",
                e
            )
        } else {
            throw e // 找不到DSL行号时 fallback到原异常
        }
    }
}

// 自定义特定类型的异常类,方便统一捕获处理
class DSLValidationException extends RuntimeException {
    DSLValidationException(String message, Throwable cause) {
        super(message, cause)
    }
}

2. 编译时提前检查(可选)

如果希望在DSL脚本编译阶段就发现错误,可以启用@DelegateTo的严格模式,配合Groovy的静态类型检查:

import groovy.lang.DelegateTo

def type(@DelegateTo(value = MyType, strict = true) Closure cl) {
    // 原有执行逻辑
}

此时,当DSL脚本中调用MyType不存在的方法时,GroovyShell在编译脚本阶段就会抛出错误,直接定位到错误行,无需等到运行时。

二、拦截非预期的闭包结构

如果需要限制DSL闭包中的操作(比如禁止嵌套闭包、禁用某些方法),可以通过以下方式实现:

1. 在委托对象中拦截未授权方法

修改MyType类,重写methodMissing方法,拦截所有未定义的方法调用并抛出异常:

class MyType {
    // 定义DSL允许的合法方法
    def configure(String param) {
        // 业务逻辑实现
    }

    // 拦截所有未定义的方法调用
    def methodMissing(String name, args) {
        throw new DSLValidationException(
            "DSL不允许调用方法'${name}',请检查闭包结构是否符合规范",
            new MissingMethodException(name, MyType, args)
        )
    }
}

2. 限制闭包嵌套(可选)

如果需要禁止在type闭包中嵌套其他闭包,可以解析闭包的AST来检查结构,这种方式适合对DSL语法有严格要求的场景:

def type(@DelegateTo(MyType) Closure cl) {
    def ast = cl.ast
    def nestedClosures = ast.statements.findAll { 
        it instanceof org.codehaus.groovy.ast.stmt.ExpressionStatement &&
        it.expression instanceof org.codehaus.groovy.ast.expr.ClosureExpression
    }
    if (!nestedClosures.isEmpty()) {
        throw new DSLValidationException("DSL不允许嵌套闭包,请移除第${nestedClosures[0].lineNumber}行的闭包代码")
    }
    // 原有闭包执行逻辑
}

三、确保GroovyShell环境的兼容性

由于你的运行环境基于GroovyShell和CompilerConfiguration,需要确保自定义的异常类、MyScript和MyType都在GroovyShell的类加载器范围内:

def config = new CompilerConfiguration(scriptBaseClass: MyScript.name)
def shell = new GroovyShell(this.class.classLoader, new Binding(), config)
// 执行DSL脚本
shell.evaluate(new File("your-dsl-script.groovy"))

内容的提问来源于stack exchange,提问作者childno͡.de

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:32:04