影评周公子 2026-03-05 21:10 采纳率: 98.8%
浏览 0
已采纳

Java 17中NashornScriptEngineFactory为何抛出ClassNotFoundException?

在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 11JEP 335标记为废弃(@Deprecated)编译期警告,运行仍可用
    Java 15JEP 372正式移除jdk.nashorn.* 模块被剔出 java.basecom.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.Context vs javax.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) 仅用于开发验证;生产环境必须使用 AllowHostAccessAllowIO 显式授权。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

问题事件

  • 已采纳回答 3月6日
  • 创建了问题 3月5日