我将一些 JSTL 逻辑重构为 Java。我当时没有考虑性能,只是假设 Java 会更快。这个假设正确吗?
每个语句包含的 JSTL 代码被重构为 Java for 循环。我的假设是在编译之前必须先将 JSTL 代码转换为 Java。
我将一些 JSTL 逻辑重构为 Java。我当时没有考虑性能,只是假设 Java 会更快。这个假设正确吗?
每个语句包含的 JSTL 代码被重构为 Java for 循环。我的假设是在编译之前必须先将 JSTL 代码转换为 Java。
你的假设(大部分)是不正确的。您的 servlet 容器(例如 Tomcat)会将所有 jsp 页面编译成 java servlet,然后编译成 java 字节码,这最终会在请求 jsp 页面时运行。所以jsp页面无论如何都会被透明地转换为java代码。jstl 自定义标签库实现逻辑和您的 java 代码之间可能存在性能差异,但这将是由于特定实现,而不是 java 与 jstl。
在 Tomcat 的默认配置下,直到第一次访问 jsp 资源时才会进行 jsp 编译,因此对于 jsp 页面的第一个请求(仅)存在启动惩罚。如果您担心第一次点击,您可以配置 jsp 页面以进行预编译。对于大多数用例来说,不值得麻烦。
不仅需要将 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;
}
我认为 JSTL 应该编译成 Java 字节码,所以我希望它们是相同的。如果您直接用 Java 编写代码,您可能会期待更好的性能,但编译器在优化方面可能比您想象的要好。
如果您真的很担心,我会运行分析器并比较结果。
会有一些开销,因为for
是一种语言结构,<c:foreach>
不会被编译成一个for
循环。JSTL标签和其他标签一样,由容器执行,容器会调用它们的生命周期和回调方法。如果您查看容器(例如 Tomcat 或 WebLogic)中生成的 JSP 源代码,您会发现使用简单标记会产生大量开销。
但我相信这种开销无法与您的数据访问、网络和……延迟相提并论。所以忽略这个小小的延迟,并使用 JSTL 和标签来使您的 JSP 更具可读性和可维护性。
顺便说一句,当您使用 JSTL(或其他标记库)时,您通常也使用JSP EL,并且评估EL有一点开销。但是,与数据访问、网络延迟和......相比,这些开销也不算在内。