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

如何在Java中运行Groovy脚本并保留状态以供后续使用?

在Java中运行Groovy脚本并保留运行状态

刚好处理过类似的场景,给你拆解成两部分详细说清楚,代码示例都给你写好了,直接能用:

1. 先搞定依赖

首先得确保项目里引入Groovy的核心库,如果用Maven的话,在pom.xml里加这段依赖(版本用最新稳定版就行):

<dependency>
    <groupId>org.codehaus.groovy</groupId>
    <artifactId>groovy-all</artifactId>
    <version>3.0.17</version>
    <type>pom</type>
</dependency>

2. 在Java中运行Groovy脚本

常用的有两种方式,选哪种看你的需求:

方式一:用Groovy原生的GroovyShell(灵活度更高)

这是Groovy官方推荐的方式,专门用来执行Groovy脚本,上手很简单:

import groovy.lang.Binding;
import groovy.lang.GroovyShell;

public class GroovyRunner {
    public static void main(String[] args) {
        // 从HTML里提取的Groovy脚本字符串
        String groovyScript = """
            def count = 0
            def increment() {
                count++
                return count
            }
            println("首次执行,count初始值: " + count)
        """;

        // 创建Binding对象,用来存脚本的变量和状态
        Binding binding = new Binding();
        // 绑定上下文到GroovyShell
        GroovyShell shell = new GroovyShell(binding);
        
        // 执行脚本
        shell.evaluate(groovyScript);
    }
}

方式二:用Java标准的ScriptEngine(兼容多脚本语言)

如果你的项目还需要支持其他脚本语言(比如JavaScript),用JSR-223标准的ScriptEngine更统一:

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class GroovyScriptEngineRunner {
    public static void main(String[] args) throws ScriptException {
        String groovyScript = """
            def count = 0
            def increment() {
                count++
                return count
            }
            println("首次执行,count初始值: " + count)
            """;

        // 获取Groovy脚本引擎
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("groovy");
        
        // 执行脚本
        engine.eval(groovyScript);
    }
}

3. 保留脚本运行状态,供后续调用

核心逻辑就是复用同一个上下文对象:GroovyShell用Binding,ScriptEngine用ScriptContext——脚本里的变量、方法都会存在这些上下文里,后续调用直接用同一个上下文就行,状态不会丢失。

用GroovyShell保留状态的完整示例

import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import groovy.lang.Script;

public class GroovyStatefulRunner {
    public static void main(String[] args) {
        String groovyScript = """
            def count = 0
            def increment() {
                count++
                return count
            }
            def getCount() {
                return count
            }
            """;

        // 重点:全程复用这个Binding对象,它是状态的载体
        Binding binding = new Binding();
        GroovyShell shell = new GroovyShell(binding);
        
        // 先解析脚本成Script实例,避免重复编译(提升性能)
        Script scriptInstance = shell.parse(groovyScript);
        // 第一次执行,初始化状态
        scriptInstance.run();

        // 后续调用脚本里的increment方法,状态会保留
        // 方式1:从Binding里拿方法引用调用
        Runnable incrementFunc = (Runnable) binding.getVariable("increment");
        incrementFunc.run(); // count变成1
        incrementFunc.run(); // count变成2

        // 获取当前count的值
        Integer currentCount = (Integer) binding.getVariable("count");
        System.out.println("当前count值:" + currentCount); // 输出2

        // 方式2:直接通过Script实例调用方法
        Object result = scriptInstance.invokeMethod("increment", null);
        System.out.println("调用increment后的值:" + result); // 输出3
    }
}

这里要注意:

  • 一定要复用同一个Binding,所有脚本的变量、方法都存在这里
  • 如果脚本需要频繁执行,先parse成Script实例,避免每次执行都重新编译脚本,性能会好很多

用ScriptEngine保留状态的完整示例

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class GroovyEngineStatefulRunner {
    public static void main(String[] args) throws ScriptException, NoSuchMethodException {
        String groovyScript = """
            def count = 0
            def increment() {
                count++
                return count
            }
            """;

        ScriptEngineManager manager = new ScriptEngineManager();
        // 重点:复用同一个ScriptEngine实例,它内部维护了脚本的状态
        ScriptEngine engine = manager.getEngineByName("groovy");
        
        // 第一次执行脚本,初始化状态
        engine.eval(groovyScript);

        // 要调用脚本里的方法,需要把engine转成Invocable接口
        Invocable invocable = (Invocable) engine;
        
        // 后续调用方法,状态会保留
        Object result1 = invocable.invokeFunction("increment");
        System.out.println("第一次调用increment:" + result1); // 输出1
        Object result2 = invocable.invokeFunction("increment");
        System.out.println("第二次调用increment:" + result2); // 输出2

        // 直接获取脚本里的count变量
        Integer currentCount = (Integer) engine.get("count");
        System.out.println("当前count值:" + currentCount); // 输出2
    }
}

这里的关键点:

  • 复用同一个ScriptEngine实例,它的内部上下文会保留脚本的所有状态
  • 通过Invocable接口可以直接调用脚本里定义的方法
  • 用engine.get("变量名")就能拿到脚本里的变量值

一些注意事项

  • 线程安全:GroovyShell和ScriptEngine都不是线程安全的,如果是多线程场景,建议每个线程用独立的实例,或者用ThreadLocal来管理上下文
  • 性能优化:频繁执行的脚本一定要先解析成Script实例(GroovyShell方式),避免重复编译
  • 状态隔离:如果同时运行多个不同的脚本,要给每个脚本单独创建Binding或ScriptContext,避免状态互相干扰

内容的提问来源于stack exchange,提问作者Roberto Ponte

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:20:49