Created
May 10, 2023 09:54
-
-
Save tonycody/aaf50a68ab1aa853dce035adf7c0c9eb to your computer and use it in GitHub Desktop.
NashornScriptEngine CompileScript Cache
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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