最近由于业务需求,帮同事把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>复制代码
虽然这个问题通过其他方式解决了。但是这个错误还是一直困扰着我。正好今天没什么事情,就决定来看看这个的问题到底出在什么地方。 因为问题页面的其他代码都没有改动过,所以我们可以很轻易的定位到我们的问题代码,也就是从 options
到 c:forEach
的改动。
我们先来了解一下jsp的编译过程:首先,客户端发送请求给web容器;然后,web容器将jsp文件编译成servlet源码,再将servlet源码编译成class文件;web容器执行class文件并将响应返回给客户端。而我们的遇到的错误信息是“unable to compile class for JSP”,而compile失败的原因呢就是 _jspService
太大了。
根据报错信息,我们得先找到_jspService这个方法。先从最简单的空页面开始调试,编译运行后在浏览器里访问该页面以生成java文件和class文件。下图就是编译后的class文件,我们成功的找到了_jspService方法,同时我们可以看到,就是这个方法输出了我们的页面。
接下来,我同时创建了两个jsp页面,里面的内容就是最上面的代码示例,一个页面用 options
标签,另一个页面用了 c:forEach
。下面贴上两个jsp的编译出来的class文件里面的 out.write()
部分代码:
第一个jsp用的是 options
标签,从图中可以看到 options
标签没有做任何的处理就编译成了class文件。
第二个文件是用的 c:forEach
,我们发现class文件里面已经没有 c:forEach
标签了,取而代之的是一个if语句,而它的判断条件是一个看起来与forEach有关的方法的返回值。来到这一步,答案其实已经呼之欲出了。我们在这个文件中继续查找,果然在下面出现了这个方法。
这个方法内部有一个 do{}while()
循环,所有的option都在这个循环中输出。由于这个方法太长了就不贴整个方法了,这个方法最终会返回 false
,然后继续执行 _jspService
方法后续的 out.write()
。
options
元素在jsp编译成class的过程中会被保留,到后面由spring MVC来处理,而每一个 c:forEach
标签在编译阶段就已经被编译成一个函数来输出目标元素。如果form表单中需要大量的 select
控件,最好能用 options
来实现。当然,最好的方法还是不要把太多的元素放到一个jsp中,可以灵活的运用 <jsp:include page="" flush="true" />
等方法来分解文件。