JS脚本在Java 8运行正常,Java 11下通过Nashorn引擎执行报错
错误成因
- 模块系统的内部API限制:Java 9引入JPMS模块系统后,JDK默认封装了所有非公开的内部API,你报错中提到的
NODE_PATH属于Nashorn的内部运行时属性,Java 8中可随意访问,Java 11中默认禁止未授权的内部属性/方法访问,直接调用就会触发unknown call type错误。 - Nashorn自身行为变更:Java 11自带的Nashorn版本收紧了全局作用域修改权限、CommonJS
require实现逻辑和内部节点访问规则,Java 8中可正常运行的隐式全局对象挂载、内部属性读取逻辑在新版本中不再被支持。 - 编译优化逻辑差异:Java 11的Nashorn即时重编译机制更严格,当检测到脚本访问未公开的内部成员时,会直接抛出类型不识别错误,而Java 8版本会做兼容降级处理。
适配解决方案
- 添加JVM启动参数放开模块限制:在Java进程启动时添加以下参数,开放Nashorn内部模块的访问权限:
如果脚本还用到其他Nashorn内部包,按照上述格式补充对应的导出/开放参数即可。--add-exports jdk.scripting.nashorn/jdk.nashorn.internal.runtime=ALL-UNNAMED --add-opens jdk.scripting.nashorn/jdk.nashorn.internal.runtime=ALL-UNNAMED - 修改JS代码规避内部属性访问:移除脚本中所有直接访问
NODE_PATH等Nashorn内部属性的逻辑,如果需要获取脚本路径等信息,改为在Java层调用__toCall方法时作为参数主动传入,不要依赖Nashorn内部属性读取。 - 显式创建兼容模式的Nashorn引擎:不要直接通过
ScriptEngineManager获取默认引擎,改用NashornScriptEngineFactory指定兼容参数创建引擎,适配Java 8的行为:NashornScriptEngineFactory factory = new NashornScriptEngineFactory(); // 开启ES6支持、脚本模式,关闭Java严格访问限制 ScriptEngine engine = factory.getEngine(new String[]{"--language=es6", "-scripting", "--no-java-strict"}); - 主动绑定全局对象:脚本中用到的
StringLeaf、Structure、test、vc等全局对象,不要依赖Nashorn隐式挂载,改为在Java层主动绑定到引擎上下文:engine.put("StringLeaf", 你的StringLeaf实现对象); engine.put("Structure", 你的Structure实现对象); engine.put("test", 你的test实例); engine.put("vc", 你的vc实例); - 使用独立版本的Nashorn依赖:如果上述方案都无法解决,可以移除JDK自带的Nashorn依赖,引入开源独立维护的Nashorn版本,不受JDK模块系统限制,完全兼容Java 8的行为,Maven依赖配置如下:
<dependency> <groupId>org.openjdk.nashorn</groupId> <artifactId>nashorn-core</artifactId> <version>15.4</version> </dependency>
内容的提问来源于stack exchange,提问作者yurii.pitomets
相关产品推荐
相关产品推荐

