在Java 17中,`NashornScriptEngineFactory` 抛出 `ClassNotFoundException` 是因Nashorn引擎自Java 15起被**标记为废弃**,并于**Java 15正式移除**(JEP 372),Java 17中已完全删除其所有相关类(如 `jdk.nashorn.api.scripting.*` 和 `com.sun.script.javascript.*`)。因此,即使代码中仍显式调用 `new NashornScriptEngineFactory()` 或通过 `ScriptEngineManager.getEngineByName("nashorn")`,JVM 将无法加载已被剔除的类,直接抛出 `ClassNotFoundException`。该异常并非配置或类路径问题,而是**平台级移除导致的必然失败**。官方推荐迁移至 GraalVM JavaScript(`org.graalvm.polyglot`)或轻量替代方案如 JSEval、Janino。若项目强依赖脚本执行,需评估兼容性重构,不可通过添加依赖“恢复”Nashorn——它已从JDK源码与运行时中彻底消失。
1条回答 默认 最新
桃子胖 2026-03-05 21:44关注一、现象层:异常表征与典型复现代码
在 Java 17 运行时,以下代码将必然触发
ClassNotFoundException:ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("nashorn"); // ← 抛出异常 // 或直接实例化(更明确暴露问题): // new NashornScriptEngineFactory().getScriptEngine();堆栈关键行示例:
java.lang.ClassNotFoundException: jdk.nashorn.api.scripting.NashornScriptEngineFactory该异常在任何标准 JDK 17+(OpenJDK、Oracle JDK、Amazon Corretto、Azul Zulu)中均100%复现,与 classpath、模块路径(–module-path)、JVM 参数无关。
二、机制层:JEP 演进与 JVM 级别移除事实
Nashorn 的消亡不是“功能降级”,而是编译器/运行时层面的物理删除:
JDK 版本 JEP 编号 状态 影响范围 Java 11 JEP 335 标记为废弃(@Deprecated) 编译期警告,运行仍可用 Java 15 JEP 372 正式移除 jdk.nashorn.*模块被剔出java.base;com.sun.script.javascript.*类从java.desktop中彻底删除Java 17 — 零残留存在 源码树中无任何 Nashorn 相关文件;JVM 启动时无法解析其 module descriptor; --list-modules输出中不可见三、认知层:常见误区辨析(为什么“加依赖”无效?)
- 误区1:“我加个
jdk.nashorn的 Maven 依赖就能恢复” → ❌ 错误。Nashorn 从未作为独立可发布 artifact 发布过;所有历史 JAR(如nashorn.jar)仅适用于 JDK 8–14,且依赖私有 JDK 内部 API(如sun.misc.Unsafe),在 Java 17 模块系统下会被IllegalAccessError阻断。 - 误区2:“用
--add-exports打通内部包即可” → ❌ 失效。Java 15+ 已将jdk.nashorn从模块图中完全移除,不存在可导出的目标模块。 - 误区3:“切换到 GraalVM JDK 就能继续用 Nashorn” → ❌ 混淆概念。GraalVM 自带的是
js引擎(基于 Graal.js),与 Nashorn 无继承关系;其 API 完全不同(org.graalvm.polyglot.Contextvsjavax.script.ScriptEngine)。
四、迁移层:替代方案选型矩阵与实操对比
以下是主流替代方案在兼容性、性能、生态、维护性四个维度的评估(★ 至 ★★★★☆):
graph LR A[脚本执行需求] --> B{脚本复杂度} B -->|简单表达式
如:x > 100 ? 'high' : 'low'| C[JSEval ★★★★☆] B -->|中等逻辑
含函数/作用域/JSON| D[GraalVM JavaScript ★★★★] B -->|Java 语法脚本
非 JS 场景| E[Janino ★★★☆] B -->|需多语言互操作
Python/R/Lua| F[GraalVM Polyglot ★★★★★] C --> G[零依赖、轻量、无 JIT 开销] D --> H[ES2022 全支持、JIT 加速、调试器集成] E --> I[编译为字节码、类型安全、无缝 Spring 表达式兼容] F --> J[跨语言对象共享、嵌入式语言沙箱、企业级监控]五、实践层:GraalVM 迁移最小可行代码(Java 17+)
替换原 Nashorn 调用的 5 行核心迁移:
// ✅ 原 Nashorn(已失效) // ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); // Object result = engine.eval("1 + 2 * 3"); // ✅ 新 GraalVM 方案(需添加依赖:org.graalvm.js:js:24.1.0) import org.graalvm.polyglot.*; Context context = Context.newBuilder("js").allowAllAccess(true).build(); Value result = context.eval("js", "1 + 2 * 3"); System.out.println(result.asInt()); // 输出:7注意:
allowAllAccess(true)仅用于开发验证;生产环境必须使用AllowHostAccess和AllowIO显式授权。本回答被题主选为最佳回答 , 对您是否有帮助呢?解决 无用评论 打赏 举报- 误区1:“我加个