转载

你了解jsp中的c:forEach吗?

最近由于业务需求,帮同事把jsp页面上的select控件进行了改动。但是在改完代码之后,在某几个拥有大量select组件的页面下出现了报错信息:_jspService超过了65535字节的限制。

// 旧的代码
  <form:options items="${opts}" itemLabel="label" itemValue="value" htmlEscape="false"/>
// 新的代码
<c:forEach items="${opts}" var="d">
    <option value="d.value" label="d.label" title="d.label"></option>
</c:forEach>复制代码

虽然这个问题通过其他方式解决了。但是这个错误还是一直困扰着我。正好今天没什么事情,就决定来看看这个的问题到底出在什么地方。 因为问题页面的其他代码都没有改动过,所以我们可以很轻易的定位到我们的问题代码,也就是从 optionsc:forEach 的改动。

我们先来了解一下jsp的编译过程:首先,客户端发送请求给web容器;然后,web容器将jsp文件编译成servlet源码,再将servlet源码编译成class文件;web容器执行class文件并将响应返回给客户端。而我们的遇到的错误信息是“unable to compile class for JSP”,而compile失败的原因呢就是 _jspService 太大了。

根据报错信息,我们得先找到_jspService这个方法。先从最简单的空页面开始调试,编译运行后在浏览器里访问该页面以生成java文件和class文件。下图就是编译后的class文件,我们成功的找到了_jspService方法,同时我们可以看到,就是这个方法输出了我们的页面。

你了解jsp中的c:forEach吗?

接下来,我同时创建了两个jsp页面,里面的内容就是最上面的代码示例,一个页面用 options 标签,另一个页面用了 c:forEach 。下面贴上两个jsp的编译出来的class文件里面的 out.write() 部分代码:

你了解jsp中的c:forEach吗?

第一个jsp用的是 options 标签,从图中可以看到 options 标签没有做任何的处理就编译成了class文件。

你了解jsp中的c:forEach吗?

第二个文件是用的 c:forEach ,我们发现class文件里面已经没有 c:forEach 标签了,取而代之的是一个if语句,而它的判断条件是一个看起来与forEach有关的方法的返回值。来到这一步,答案其实已经呼之欲出了。我们在这个文件中继续查找,果然在下面出现了这个方法。

你了解jsp中的c:forEach吗?

这个方法内部有一个 do{}while() 循环,所有的option都在这个循环中输出。由于这个方法太长了就不贴整个方法了,这个方法最终会返回 false ,然后继续执行 _jspService 方法后续的 out.write()

总结

options 元素在jsp编译成class的过程中会被保留,到后面由spring MVC来处理,而每一个 c:forEach 标签在编译阶段就已经被编译成一个函数来输出目标元素。如果form表单中需要大量的 select 控件,最好能用 options 来实现。当然,最好的方法还是不要把太多的元素放到一个jsp中,可以灵活的运用 <jsp:include page="" flush="true" /> 等方法来分解文件。

原文  https://juejin.im/post/5be00237e51d451c6a14b911
正文到此结束
Loading...