3

我将一些 JSTL 逻辑重构为 Java。我当时没有考虑性能,只是假设 Java 会更快。这个假设正确吗?

每个语句包含的 JSTL 代码被重构为 Java for 循环。我的假设是在编译之前必须先将 JSTL 代码转换为 Java。

4

5 回答 5

2

你的假设(大部分)是不正确的。您的 servlet 容器(例如 Tomcat)会将所有 jsp 页面编译成 java servlet,然后编译成 java 字节码,这最终会在请求 jsp 页面时运行。所以jsp页面无论如何都会被透明地转换为java代码。jstl 自定义标签库实现逻辑和您的 java 代码之间可能存在性能差异,但这将是由于特定实现,而不是 java 与 jstl。

在 Tomcat 的默认配置下,直到第一次访问 jsp 资源时才会进行 jsp 编译,因此对于 jsp 页面的第一个请求(仅)存在启动惩罚。如果您担心第一次点击,您可以配置 jsp 页面以进行预编译。对于大多数用例来说,不值得麻烦。

于 2013-02-23T22:56:17.917 回答
1

不仅需要将 JSTL 代码转换为 java 源代码然后编译(这不会影响运行时性能),而且由于创建Tag实例和访问请求属性(它们是局部变量)而存在少量开销在一个普通的 Java 循环中)。我认为大部分成本可以通过编译器优化来隐藏,例如逃逸分析,和/或与其他“成本”(例如网络延迟)相比足够小,但无论如何它都会造成损失。

出于好奇,我查看了一个由 Tomcat 6 编译的 JSP。这是从一个简单的 c:forEach 标签生成的源代码...

  private boolean _jspx_meth_c_005fforEach_005f0(PageContext _jspx_page_context)
          throws Throwable {
    PageContext pageContext = _jspx_page_context;
    JspWriter out = _jspx_page_context.getOut();
    //  c:forEach
    org.apache.taglibs.standard.tag.el.core.ForEachTag _jspx_th_c_005fforEach_005f0 = (org.apache.taglibs.standard.tag.el.core.ForEachTag) _005fjspx_005ftagPool_005fc_005fforEach_0026_005fvar_005fitems.get(org.apache.taglibs.standard.tag.el.core.ForEachTag.class);
    _jspx_th_c_005fforEach_005f0.setPageContext(_jspx_page_context);
    _jspx_th_c_005fforEach_005f0.setParent(null);
    _jspx_th_c_005fforEach_005f0.setVar("session");
    _jspx_th_c_005fforEach_005f0.setItems("${session_info}");
    int[] _jspx_push_body_count_c_005fforEach_005f0 = new int[] { 0 };
    try {
      int _jspx_eval_c_005fforEach_005f0 = _jspx_th_c_005fforEach_005f0.doStartTag();
      if (_jspx_eval_c_005fforEach_005f0 != javax.servlet.jsp.tagext.Tag.SKIP_BODY) {
        do {
          out.write(" \r\n");
          out.write(" <TR>\r\n");
          out.write("     <TD>"); //etc...
          if (_jspx_meth_c_005fout_005f0(_jspx_th_c_005fforEach_005f0, _jspx_page_context, _jspx_push_body_count_c_005fforEach_005f0))
            return true;
          out.write("\r\n");
          out.write("     <TD>");
          if (_jspx_meth_c_005fif_005f0(_jspx_th_c_005fforEach_005f0, _jspx_page_context, _jspx_push_body_count_c_005fforEach_005f0))
            return true;
          int evalDoAfterBody = _jspx_th_c_005fforEach_005f0.doAfterBody();
          if (evalDoAfterBody != javax.servlet.jsp.tagext.BodyTag.EVAL_BODY_AGAIN)
            break;
        } while (true);
      }
      if (_jspx_th_c_005fforEach_005f0.doEndTag() == javax.servlet.jsp.tagext.Tag.SKIP_PAGE) {
        return true;
      }
    } catch (Throwable _jspx_exception) {
      while (_jspx_push_body_count_c_005fforEach_005f0[0]-- > 0)
        out = _jspx_page_context.popBody();
      _jspx_th_c_005fforEach_005f0.doCatch(_jspx_exception);
    } finally {
      _jspx_th_c_005fforEach_005f0.doFinally();
      _005fjspx_005ftagPool_005fc_005fforEach_0026_005fvar_005fitems.reuse(_jspx_th_c_005fforEach_005f0);
    }
    return false;
  }
于 2013-02-23T23:02:25.613 回答
1

我认为 JSTL 应该编译成 Java 字节码,所以我希望它们是相同的。如果您直接用 Java 编写代码,您可能会期待更好的性能,但编译器在优化方面可能比您想象的要好。

如果您真的很担心,我会运行分析器并比较结果。

于 2013-02-23T22:57:06.507 回答
0

Java scriptlet 将比 JSTL 代码更快,因为它是纯 Java 代码,并且在 JSP 转换为 Servlet 类时不需要任何解析。不过,您应该避免使用此处解释的 scriptlet 。

如果您使用Facelets而不是 JSP 作为表示层的技术,则根本不需要使用 scriptlet。

恕我直言,我怀疑解析 JSTL(和其他标签)的 100 或 200 毫秒开销会对您的应用程序产生重大影响。

于 2013-02-23T23:00:34.417 回答
0

会有一些开销,因为for是一种语言结构,<c:foreach>不会被编译成一个for循环。JSTL标签和其他标签一样,由容器执行,容器会调用它们的生命周期和回调方法。如果您查看容器(例如 Tomcat 或 WebLogic)中生成的 JSP 源代码,您会发现使用简单标记会产生大量开销。

但我相信这种开销无法与您的数据访问、网络和……延迟相提并论。所以忽略这个小小的延迟,并使用 JSTL 和标签来使您的 JSP 更具可读性和可维护性。

更新:

顺便说一句,当您使用 JSTL(或其他标记库)时,您通常也使用JSP EL,并且评估EL有一点开销。但是,与数据访问、网络延迟和......相比,这些开销也不算在内。

于 2013-02-23T23:22:33.727 回答