nashorn在openjdk 15+已彻底移除,必须迁移至graalvm.js;scriptengine.getenginebyname("javascript")报错是因引擎实现被剥离,需改用"graal.js"并适配polyglot api、显式绑定变量、替换load()等nashorn特有语法。

直接结论:Nashorn在OpenJDK 15+已彻底移除,无法“保留使用”,必须迁移;最稳妥、生产验证过的路径是切换到GraalVM.js,而非降级JDK或引入兼容模块。
ScriptEngine.getEngineByName("javascript") 报 ScriptEngineNotFoundException
这是最典型的表象——ScriptEngineManager 找不到引擎名 "javascript" 或 "nashorn"。不是配置问题,是JDK本身没这个实现类了。
- OpenJDK 15起,
jdk.nashorn.api.scripting.NashornScriptEngineFactory已从JVM中剥离,javax.script接口还在,但注册的引擎为空 - 别试
-Djava.ext.dirs或手动加nashorn.jar:JDK 22+ 连jdk.nashorn.*包名都已被模块系统封禁,类加载会直接失败 - 若项目仍用
getEngineByName("nashorn"),需全局替换为getEngineByName("graal.js")(注意名称不是"javascript")
GraalVM.js 替换时的 Polyglot API 适配要点
用 ScriptEngine 接口能跑通,但行为和性能有隐性差异,不能只改名字。
-
eval()中的全局变量默认不共享:Nashorn里engine.eval("a = 1"); engine.eval("print(a)")能输出,GraalVM.js 默认隔离,需显式传入Bindings或启用js.nashorn-compat模式(不推荐长期用) -
load()函数不存在:Nashorn的load("xxx.js")是非标准扩展,GraalVM.js需改用Context.newBuilder("js").allowIO(true).build().eval("js", Files.readString(path)) - Java互调语法微调:Nashorn中
Java.type("java.util.ArrayList")仍可用,但推荐统一用Java.type+ 完整类名;访问静态字段需加.static,如java.lang.System.static.out.println() - 启动开销略高:首次
Context.create("js")较慢,建议复用Context实例(线程安全),不要每次eval都新建
Maven依赖与运行时JDK选择
不强制要求用GraalVM JDK,但必须显式声明依赖,且要注意版本对齐。
- 添加
org.graalvm.js:js和org.graalvm.js:js-script-engine(二者缺一不可),版本建议 24.2.0+(对应JDK 21+ 兼容性最佳) - 若用标准OpenJDK 17/21,无需替换JDK,GraalVM.js纯Java实现可运行;但用GraalVM JDK能启用AOT编译和更优GC策略
- 排除旧冲突包:检查
mvn dependency:tree是否意外拉入com.oracle.substratevm或老版truffle-api,它们与新版js不兼容 - Spring Boot项目注意:Boot 3.x 自动配置
ScriptEngineBean 时仍尝试找"javascript",需手动@Bean注册GraalJSScriptEngine并设名称为"graal.js"
遗留脚本里那些“Nashorn特供语法”怎么处理
真正卡点往往不在Java侧,而在JS脚本本身——尤其是用了Nashorn私有API的业务逻辑。
-
__DIR__、__FILE__这类全局变量:GraalVM.js不提供,需在Bindings中预置Map.of("__DIR__", "/path") - Java数组操作:Nashorn允许
arr.length = 0清空,GraalVM.js中length是只读属性,得用arr.splice(0) - 日期解析宽松模式:Nashorn的
Date.parse("2023-01-01")容错强,GraalVM.js严格遵循ISO 8601,非标准格式需先用java.time.LocalDate.parse()转再传入JS - 正则标志
g/i大小写不敏感:GraalVM.js区分大小写,/abc/gi合法,但/abc/GI会报错
复杂脚本建议先用 Context.newBuilder("js").option("js.nashorn-compat", "true") 快速兜底,再逐步清理兼容开关——它只是过渡手段,开启后会禁用部分优化,且未来版本可能移除。











