当前位置: 首页 > 知识库问答 >
问题:

在Servlet中重用Nashorn ScriptEngine[重复]

林弘壮
2023-03-14

我想在servlet中执行JavaScript。是否可以在所有servlet调用中重用相同的脚本引擎?Servlet实例由多个线程共享。这是否需要为每个请求创建一个新的脚本引擎?这将是一个不可接受的表现惩罚。例如,下面的代码是否保存?

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);
    }
}

这样安全吗?

共有1个答案

施利
2023-03-14

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);
    }
  }
}
 类似资料:
  • 问题内容: 我想在Servlet中执行JavaScript。是否可以在所有servlet调用中重用相同的脚本引擎?Servlet实例由多个线程共享。这是否需要为每个请求创建一个新的脚本引擎?那将是不可接受的性能损失。例如,以下代码是否保存? } 如果这样做不安全,那么避免每个请求创建引擎的最佳方法是什么?使用引擎池? 编辑: 是否可以对所有Servlet请求重用一个同一个引擎和一个同一个JavaS

  • xml文件位于我的项目的中。我正在使用Eclipse并运行Tomcat(它不是通过Eclipse安装的,我更希望它是一个单独的安装)。 当表单页面提交给servlet时,它就不起作用了。我每次都得到一个404错误。我遇到这个问题有一段时间了。谁来帮帮我。

  • 现在,可重用在JavaBean中意味着什么。我们不能重用servlet吗? 如果有人能用很少的例子来解释这一点,我将不胜感激。

  • 主要内容:重定向的工作流程,转发和重定向的区别,response.sendRedirect(),示例重定向属于客户端行为。服务器在收到客户端请求后,会通知客户端浏览器重新向另外一个 URL 发送请求,这称为请求重定向。它本质上是两次 HTTP 请求,对应两个 request 对象和两个 response 对象。 重定向的工作流程 重定向的工作流程如下: 用户在浏览器中输入 URL,请求访问服务器端的 Web 资源。 服务器端的 Web 资源返回一个状态码为 302 的响应信息,该响应的含义为:通

  • 我开始在eclipse中使用servlet开发java web应用程序,并在本地主机上用tomcat服务器测试它。我已经在tomcat中部署了应用程序,但是当我尝试在浏览器中加载目标url时,我得到了以下堆栈跟踪: ImageServlet类非常清楚地位于我的eclipse工作区的myproject/src/test文件夹中,其中myproject是eclipse项目的名称,test是包。 web

  • 我有2个问题 1)我有一个login.jsp页和索引页。一旦我验证了用户(在servlet ie中),用户将被重定向到index.jsp页面。我用servlet的这条线重定向它 这个过程运行良好。但是当我刷新页面时,我会被重定向回登录页面。另外,我注意到在index.jsp页面上,我得到的名字而不是index.jsp,即我的servlet名称得到displayed.for这样的例子得到代替显示 2