问题
I have an existing Spring Boot application that is non-modular and uses Nashorn. The application works well on Java 14.
After adding the Maven coordinates of the new Nashorn available for Java 15, the application fails while starting the script engine.
public static void main(String[] args) throws ScriptException {
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("nashorn");
engine.eval("print('Hello, World!');");
}
Error message:
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)
Is it required to modularize the whole project to make use of Nashorn?
回答1:
According to 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
For Maven, include the below dependency into your pom.xml
<dependency>
<groupId>org.openjdk.nashorn</groupId>
<artifactId>nashorn-core</artifactId>
<version>15.0</version>
</dependency>
For Gradle, include dependency below into your build.gradle
implementation 'org.openjdk.nashorn:nashorn-core:15.0'
Unfortunately, Standalone Nashorn is only usable as a JPMS module. So you might need to follow the solution stated in https://stackoverflow.com/a/46289257 to make it work with a non-modular application.
From the given class xxxxx.yyyy.service.JavaScriptServiceImpl
and based on feedback from @JornVernee and @AttilaSzegedi, the command line should look like
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
回答2:
Nashorn maintainer here.
It indeed seems to be an issue with Spring Boot not loading Nashorn as a JPMS module. Nashorn exports itself as a scripting engine to be found by the javax.script.ScriptEngineManager
through a "provides" entry in its module-info.java. It does not use the older, non-modular export mechanism of declaring itself through a relevant META-INF/services/…
entry in its JAR file. This means that if the JAR is not loaded as a JPMS module, script engine manager will not discover it. (NB: even if it redundantly had that META-INF/services
entry, it wouldn't help because Nashorn relies on being loaded as a module; as code that used to ship with JDK, it has been a module since Java 9… it'd be somewhat hard to undo that now.)
I created a small test application that confirms this is the case. I'm trying to enlist some people who work on Boot to help me get to the bottom of this. It's complicated by the fact that Boot creates a fat JAR file and packages all of its dependencies into it and then manages their loading, so it's not like you can "just" modify the modulepath yourself on startup.
Hopefully there's a way to tell Boot to load a dependency as a module; my attempts to find it through Google haven't proved fruitful so far.
回答3:
I just released Nashorn 15.1 which makes it possible for Nashorn to function when loaded through classpath instead of through modulepath. I tested it with a small Spring Boot application of my own, and it works.
来源:https://stackoverflow.com/questions/65265629/how-to-use-nashorn-in-java-15-and-later