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

How to use Nashorn in Java 15 and later?

我有一个现有的 Spring 非模块化引导应用程序,它使用 Nashorn。该应用程序在 Java 14.

上运行良好

添加可用于 Java15 的新 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?

根据JEP 372, Nashorn had been removed from JDK 15 but you can get latest nashorn from https://search.maven.org/artifact/org.openjdk.nashorn/nashorn-core/15.0/jar

对于 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'

不幸的是,Standalone Nashorn is only usable as a JPMS module. So you might need to follow the solution stated in 使其与非模块化应用程序一起工作。

根据给定的 class 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

这里是 Nashorn 维护者。

Spring 引导未将 Nashorn 作为 JPMS 模块加载似乎确实是一个问题。 Nashorn 将自身导出为脚本引擎,可由 javax.script.ScriptEngineManager 通过 "provides" entry in its module-info.java 找到。它不使用通过其 JAR 文件中的相关 META-INF/services/… 条目声明自身的较旧的非模块化导出机制。这意味着如果 JAR 没有作为 JPMS 模块加载,脚本引擎管理器将不会发现它。 (注意:即使它冗余地具有 META-INF/services 条目,也无济于事,因为 Nashorn 依赖于作为模块加载;作为用于 JDK 的代码,它自从Java 9…现在要撤消它有点困难。)

我创建了一个 small test application 来确认是这种情况。我正试图招募一些在 Boot 上工作的人来帮助我弄清这件事的真相。由于 Boot 创建一个胖 JAR 文件并将其所有依赖项打包到其中,然后管理它们的加载,因此这很复杂,因此您不能在启动时“只”自己修改模块路径。

希望有一种方法可以告诉 Boot 将依赖项作为模块加载;我通过 Google 找到它的尝试到目前为止还没有成功。

我刚刚发布了 Nashorn 15.1 这使得 Nashorn 在通过类路径而不是通过模块路径加载时可以正常运行。我用我自己的 Spring 启动应用程序对其进行了测试,它可以正常工作。