Skip to content

Instantly share code, notes, and snippets.

@tonycody
Created May 10, 2023 09:54
Show Gist options
  • Save tonycody/aaf50a68ab1aa853dce035adf7c0c9eb to your computer and use it in GitHub Desktop.
Save tonycody/aaf50a68ab1aa853dce035adf7c0c9eb to your computer and use it in GitHub Desktop.
NashornScriptEngine CompileScript Cache
import cn.hutool.core.convert.Convert;
import jdk.nashorn.api.scripting.NashornScriptEngine;
import lombok.NonNull;
import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleBindings;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
/**
* 这是一个 {@link NashornScriptEngine } 执行脚本缓存 <p>
* 说明:使用 {@link NashornScriptEngine } 执行 <code>JavaScript</code> 脚本时,每次会 compile 脚本,影响性能的同时还存在CAS问题<br>
* 因此,缓存编译后的Script对象非常有必要,尤其在高并发系统中使用 {@link NashornScriptEngine}
*
* @author XS <[email protected]>
* @date 2023/05/10
*/
class Scratch {
public static void main(String[] args) throws ScriptException {
String scriptText = "Math.floor(100+13*Math.pow(1.014,data.level)*(data.level-1))";
Bindings bindings = new SimpleBindings();
Map<Object, Object> data = new HashMap<>();
data.put("level", 10);
bindings.put("data", data);
BigDecimal eval = ScriptHolder.eval(1000L, scriptText, bindings, Convert::toBigDecimal);
System.out.println(eval);
bindings = new SimpleBindings();
data = new HashMap<>();
data.put("level", 10);
bindings.put("data", data);
eval = ScriptHolder.eval(1000L, scriptText, bindings, Convert::toBigDecimal);
System.out.println(eval);
}
}
class ScriptHolder {
/** 脚本引擎 */
private static final Compilable SCRIPT_ENGINE = (Compilable) new ScriptEngineManager().getEngineByName("nashorn");
/** 编译脚本缓存 */
private static final Map<Long, CompiledScript> COMPILED_SCRIPT_CACHE = new HashMap<>();
/**
* 执行脚本 <p>
*
* 如果存在脚本编译缓存,直接拿编译后的Script实例来执行脚本,否则缓存
* @param scriptId 脚本id,缓存Key
* @param scriptText 脚本文本
* @param bindings 绑定参数对象,无参数传一个 bindings 实例
* @param converter 类型转换
* @return {@link Object}
* @throws ScriptException 脚本异常
*/
public static <T> T eval(
@NonNull Long scriptId,
@NonNull String scriptText,
@NonNull Bindings bindings,
@NonNull Function<Object, T> converter)
throws ScriptException {
CompiledScript compiledScript = COMPILED_SCRIPT_CACHE.get(scriptId);
return converter.apply(Optional.ofNullable(compiledScript)
.orElseGet(() -> {
CompiledScript script;
try {
script = SCRIPT_ENGINE.compile(scriptText);
} catch (ScriptException e) {
throw new RuntimeException(e);
}
COMPILED_SCRIPT_CACHE.put(scriptId, script);
return script;
})
.eval(bindings));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment