本文介绍如何在 GroovyShell 中安全、高效地实现类似 $ROOT$.xxx 的简洁表达式语法,避免 with{} 带来的性能损耗,核心是利用 DelegatingScript 将脚本上下文委托给指定对象,使属性/方法调用自动解析到根对象上。
本文介绍如何在 groovyshell 中安全、高效地实现类似 `$root$.xxx` 的简洁表达式语法,避免 `with{}` 带来的性能损耗,核心是利用 `delegatingscript` 将脚本上下文委托给指定对象,使属性/方法调用自动解析到根对象上。
在 Java 应用中嵌入 Groovy 表达式时,常需将外部 Java 对象作为“根上下文”供脚本访问。传统做法(如 "$ROOT$.empty")虽安全但冗长;而 "$ROOT$.with{ empty == true }" 虽语法简洁,却因闭包创建与动态委托开销导致性能下降约 10 倍(JMH 实测)。根本问题在于:Groovy 默认脚本不支持“隐式接收者”,即无法让 empty == true 自动解析为 rootObject.empty == true。
解决方案:使用 DelegatingScript 作为脚本基类
Groovy 提供了 groovy.util.DelegatingScript —— 一个专为委托设计的脚本基类。当脚本继承它时,所有未显式限定的方法调用、属性访问、变量引用均自动转发至其 delegate 对象(即你的根对象),无需 with{} 包裹,也无需 $ROOT$ 前缀。
✅ 关键优势:
实现步骤(Java 示例):
import groovy.lang.GroovyShell;import groovy.util.DelegatingScript;import org.codehaus.groovy.control.CompilerConfiguration;import java.util.Collections;public class DelegatingGroovyExample { public static void main(String[] args) { // 1. 配置 GroovyShell,指定脚本基类为 DelegatingScript CompilerConfiguration config = new CompilerConfiguration(); config.setScriptBaseClass(DelegatingScript.class.getName()); GroovyShell shell = new GroovyShell( DelegatingGroovyExample.class.getClassLoader(), new Binding(), config ); // 2. 解析表达式(注意:此处是表达式字符串,非完整脚本) String snippet = "empty == true"; // ✅ 简洁语法! Object script = shell.parse(snippet); // 3. 强制转换并设置 delegate(即你的根对象) if (script instanceof DelegatingScript) { DelegatingScript ds = (DelegatingScript) script; ds.setDelegate(Collections.emptyList()); // 根对象:空列表 // 4. 执行并获取结果 Boolean result = (Boolean) ds.run(); System.out.println("Result: " + result); // 输出:true } }}
⚠️ 注意事项:
总结
DelegatingScript 是 Groovy 提供的轻量级、高性能委托机制,完美替代低效的 with{} 模式。它让嵌入式 Groovy 表达式既保持语义清晰(empty == true),又具备生产级性能。对于已知根对象类型的场景(如统一处理 List、Map 或领域模型实例),该方案是当前最推荐的隐式上下文绑定实践。