如何在Java 15及更高版本中使用Nashorn?

24

我有一个现有的Spring Boot应用程序,该应用程序是非模块化的,并使用Nashorn。该应用程序在Java 14上运行良好。

在添加了适用于Java 15的新Nashorn的Maven坐标后,应用程序在启动脚本引擎时失败了。

public static void main(String[] args) throws ScriptException {
    ScriptEngineManager factory = new ScriptEngineManager();
    ScriptEngine engine = factory.getEngineByName("nashorn"); 
    engine.eval("print('Hello, World!');");
} 

错误信息:

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "javax.script.ScriptEngine.eval(String)" because "engine" is null
    at xxxxx.yyyy.service.JavaScriptServiceImpl.main(JavaScriptServiceImpl.java:52)

需要将整个项目模块化以利用Nashorn吗?


3
Java 15中已经移除了Java Nashorn(请参见JEP/732)。自Java 11以来,您应该已经收到有关此警告的通知。 - Codo
3个回答

18
根据JEP 372,Nashorn已经从JDK 15中移除,但你可以从https://search.maven.org/artifact/org.openjdk.nashorn/nashorn-core/15.0/jar获取最新的Nashorn。
对于Maven,请将以下依赖项包含在你的pom.xml中。
<dependency>
  <groupId>org.openjdk.nashorn</groupId>
  <artifactId>nashorn-core</artifactId>
  <version>15.0</version>
</dependency>

对于Gradle,将以下依赖项包含在您的build.gradle中:

implementation 'org.openjdk.nashorn:nashorn-core:15.0'

不幸的是,独立Nashorn只能作为JPMS模块使用。因此,您可能需要按照https://dev59.com/w1YO5IYBdhLWcg3wI-Rw#46289257中提到的解决方案来使其与非模块化应用程序一起使用。

根据给定的类xxxxx.yyyy.service.JavaScriptServiceImpl和@JornVernee和@AttilaSzegedi的反馈,命令行应如下所示:

jdk-15.0.1/bin/java -classpath /home/nashorn-helloworld/target/classes --module-path /home/org/openjdk/nashorn/nashorn-core/15.0:/home/org/ow2/asm/asm/7.3.1:/home/org/ow2/asm/asm-analysis/7.3.1:/home/org/ow2/asm/asm-commons/7.3.1:/home/org/ow2/asm/asm-tree/7.3.1/home/org/ow2/asm/asm-util/7.3.1 --add-modules org.openjdk.nashorn xxxxx.yyyy.service.JavaScriptServiceImpl

@JornVernee 我尝试了作为参数的 --add-modules,但它没有起效。 - Aswath Murugan
jdk-15.0.1/bin/java -classpath /home/nashorn-helloworld/target/classes HelloWorld --module-path /home/org/openjdk/nashorn/nashorn-core/15.0/nashorn-core-15.0.jar:/home/org/ow2/asm/asm/7.3.1/asm-7.3.1.jar:/home/org/ow2/asm/asm-commons/7.3.1/asm-commons-7.3.1.jar:/home/org/ow2/asm/asm-tree/7.3.1/asm-tree-7.3.1.jar:/home/org/ow2/asm/asm-util/7.3.1/asm-util-7.3.1.jar --add-modules org.openjdk.nashorn --add-modules org.ow2.asm --add-modules org.ow2.asm.asm-commons --add-modules org.ow2.asm.asm-tree --add-modules org.ow2.asm.asm-util - Aswath Murugan
3
你只需要加上 --add-modules org.openjdk.nashorn 参数,但是你将 --module-path 参数当作程序参数而非虚拟机选项传递,在命令的最后放置了 HelloWorld(我假设这是你的主类)。 - Jorn Vernee
2
此外,--module-path 是一个目录列表;您只需要列出模块化 JAR 文件所在的目录,而不是 JAR 文件本身。 - Attila Szegedi

17
我刚刚发布了Nashorn 15.1版本,可以在通过类路径而非模块路径加载时使Nashorn正常工作。我使用自己的一个小型Spring Boot应用进行了测试,并且它可以正常运行。

1
抱歉有点傻,但我刚刚升级到JDK 15,我本以为只需添加依赖项org.openjdk.nashorn:nashorn-core:15.2即可。然而,我一直在ClassFilter类上收到NoClassDefFound错误。我不需要在这种情况下添加add-modules选项,这样想是不对的吗? - StFS
1
看起来运行得很好,谢谢Attila。首先我尝试使用独立的Nashorn 15.2版本而没有模块,但它似乎陷入了无限等待/循环而没有错误。 事实上,Maven Shade插件似乎删除了打包中必需的依赖项。 所有依赖项都已打包,可以在win/lin/mac jdks上测试并正常工作。 - ron190

15

我是Nashorn维护人员。

问题确实出在Spring Boot无法将Nashorn加载为JPMS模块上。Nashorn通过其module-info.java中的“provides”条目,将自己导出为可由javax.script.ScriptEngineManager发现的脚本引擎。它没有使用较旧、非模块化的导出机制,即通过其JAR文件中相关的META-INF/services/…条目来声明自己。这意味着如果未将JAR作为JPMS模块加载,则脚本引擎管理器将无法发现它。(顺便说一句:即使它多余地具有那个META-INF/services条目,也没用,因为Nashorn依赖于作为模块加载;作为曾经随JDK发布的代码,自Java 9以来,它已成为一个模块...现在要撤销它可能有点困难)

我创建了一个小的测试应用程序,证明了这一点。我正在尝试征求一些在Boot上工作的人帮助我查明问题的根源。由于Boot创建了一个fat JAR文件并将其所有依赖项打包到其中,然后管理它们的加载,所以你不能“只是”在启动时自己修改模块路径。

希望有一种方法可以告诉Boot将一个依赖项作为模块加载;我通过谷歌搜索尝试了一下,但目前还没有结果。


嗨@attila,我们在OpenJDK实现中有javax.script.invocable的替代方案吗? - Ayush v
嗨@Attila,我们在Open JDK实现中是否有javax.script.invocable的替代方法?在我的当前JAVA 8实现中,我正在使用javax.script.Invocable。我们如何迁移这个?我们如何将它迁移到Open JDK实现? - Ayush v

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接