java - 在 Servlet 中重用 Nashorn ScriptEngine

标签 java servlets nashorn

<分区>

我想在 servlet 中执行 JavaScript。是否可以在所有 servlet 调用中重用相同的脚本引擎? Servlet 实例由多个线程共享。这是否需要为每个请求创建一个新的脚本引擎?那将是 Not Acceptable 性能损失。举个例子,下面的代码保存了吗?

public class MyServlet extends HttpServlet {

private ScriptEngineManager factory;
private ScriptEngine engine;

@Override
public void init() throws ServletException {
    factory = new ScriptEngineManager();
    engine = factory.getEngineByName("nashorn");
}

@Override
public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
    try (PrintWriter writer = res.getWriter()) {
        ScriptContext newContext = new SimpleScriptContext();
        newContext.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
        Bindings engineScope = newContext.getBindings(ScriptContext.ENGINE_SCOPE);
        engineScope.put("writer", writer);
        Object value = engine.eval("writer.print('Hello, World!');", engineScope);
        writer.close();
    } catch (IOException | ScriptException ex) {
        Logger.getLogger(AsyncServlet.class.getName()).log(Level.SEVERE, null, ex);
    }
}

如果这不安全,避免为每个请求创建引擎的最佳方法是什么?使用引擎池?

编辑: 是否可以为所有 servlet 请求重用一个相同的引擎和一个相同的 JavaScriptObject,它作为 JS 函数的评估结果,如果该函数不更改任何共享对象而仅使用给定的参数称呼?请看上面示例的以下改编:

public class MyServlet extends HttpServlet {

private ScriptEngineManager factory;
private ScriptEngine engine;
private ScriptObjectMirror script;

@Override
public void init() throws ServletException {
    try {
        factory = new ScriptEngineManager();
        engine = factory.getEngineByName("nashorn");
        script = (ScriptObjectMirror)engine.eval("function(writer) {writer.print('Hello, World!');}");
    } catch (ScriptException ex) {
        Logger.getLogger(MyServlet.class.getName()).log(Level.SEVERE, null, ex);
    }
}

@Override
public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
    try (PrintWriter writer = res.getWriter()) {
        script.call(null, writer);
        writer.close();
    } catch (IOException ex) {
        Logger.getLogger(MyServlet.class.getName()).log(Level.SEVERE, null, ex);
    }
}

这样安全吗?

最佳答案

javax.script.ScriptEngineFactory 中有一个方法 getParameter(String key) .

使用特殊键 THREADING,您可以获得该特定发动机工厂的线程信息。

这个小程序为每个注册的发动机工厂打印出这些信息:

import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;

public class ScriptEngineTest {
  public static void main(String[] args) {
    final ScriptEngineManager mgr = new ScriptEngineManager();
    for(ScriptEngineFactory fac: mgr.getEngineFactories()) {
      System.out.println(String.format("%s (%s), %s (%s), %s", fac.getEngineName(),
          fac.getEngineVersion(), fac.getLanguageName(),
          fac.getLanguageVersion(), fac.getParameter("THREADING")));
    }
  }
}

对于 Java 7,它是:

Mozilla Rhino (1.7 release 3 PRERELEASE), ECMAScript (1.8), MULTITHREADED

对于 Java 8:

Oracle Nashorn (1.8.0_25), ECMAScript (ECMA - 262 Edition 5.1), null

null 表示引擎实现不是线程安全的。

在您的 servlet 中,您可以使用 ThreadLocal为每个线程保留一个单独的引擎,允许为同一线程服务的后续请求重用该引擎。

public class MyServlet extends HttpServlet {

  private ThreadLocal<ScriptEngine> engineHolder;

  @Override
  public void init() throws ServletException {
    engineHolder = new ThreadLocal<ScriptEngine>() {
      @Override
      protected ScriptEngine initialValue() {
        return new ScriptEngineManager().getEngineByName("nashorn");
      }
    };
  }

  @Override
  public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
    try (PrintWriter writer = res.getWriter()) {
      ScriptContext newContext = new SimpleScriptContext();
      newContext.setBindings(engineHolder.get().createBindings(), ScriptContext.ENGINE_SCOPE);
      Bindings engineScope = newContext.getBindings(ScriptContext.ENGINE_SCOPE);
      engineScope.put("writer", writer);
      Object value = engineHolder.get().eval("writer.print('Hello, World!');", engineScope);
      writer.close();
    } catch (IOException | ScriptException ex) {
      Logger.getLogger(MyServlet.class.getName()).log(Level.SEVERE, null, ex);
    }
  }
}

关于java - 在 Servlet 中重用 Nashorn ScriptEngine,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27710407/

相关文章:

java - Oracle/Java 应用,推荐架构

tomcat - 不同部署环境的默认 servlet 映射

java - 调用匿名 JavaScript 函数

java - 将 javabean 的图形序列化为 xml,每个 java 实例都有单独的 xml 文件

java - 我需要能够捕获小数点后仅 2 位数字的小数 java,尝试了这个

java - 将结果写入 volatile 变量以防止测试中的死代码消除

Java,nashorn访问另一个js文件

java - Jenkins:作业页面的侧栏中缺少 SonarQube 链接

java - 如何在 servlet 的 jsp 中显示警报,然后重定向到另一个 jsp?

java - IntelliJ : Put some dependencies on the module path in non-modular application