XHTML页面方法未按层级顺序执行异常排查
问题根源
这个执行顺序错乱和你写的代码上下位置无关,是JSF的生命周期机制+错误的视图层写法共同导致的:
<c:forEach>这类JSTL标签属于标签处理器,会在视图构建阶段就完成执行,早于页面组件渲染阶段。标签在初始化时会优先对items属性绑定的表达式求值,这个求值过程不会按照你写的DOM从上到下顺序走,甚至会优先把所有层级标签的items属性都求值完,才会执行标签体内的表达式。这就是为什么内层的cursoAlumnoView.listarEntrega()会比外层写在它前面的方法先执行。- 你把带副作用的状态修改逻辑
#{cursoAlumnoView.setIdusu(cur.idUsuario)}直接写在标签体里,本身就不符合JSF使用规范。这类表达式的执行时机没有任何保障,你靠它先执行给后续方法传参的逻辑从根上就不成立。 - 你把数据查询、平均值计算这类业务逻辑全写在EL表达式里,这类方法在JSF的整个请求生命周期里可能被反复调用多次,不仅顺序不可控,还会产生重复查库的性能问题。
修复方案
不要在视图层承载业务逻辑和状态修改逻辑,所有数据准备工作全部移到后端Bean完成,视图层只负责渲染已经准备好的现成数据:
- 把
listaCursoAlumno()、listarEntrega()、devolverPromedioLista()、listaCursoAsignatura()这类查询、计算逻辑,统一放到对应Bean的@PostConstruct初始化方法里执行,不要在EL中直接调用这类方法。 - 删掉视图层里的setter调用,不要靠修改Bean全局变量的方式给内层方法传参。外层遍历学生时,提前把每个学生对应的平均分、作业交付列表、缺考数量都组装好,存在学生对象的扩展字段或者专门的视图DTO里。
- 修正后的视图层代码参考,全程只做属性取值,不调用任何业务方法:
<c:forEach items="#{cursoAlumnoView.listaCursoAlumno}" var="cur"> <tr class="text-center"> <td>#{cur.idUsuario.idUsuario}</td> <td>#{cur.idUsuario.nombres} #{cur.idUsuario.apellidos}</td> <td>#{cur.promedio}</td> <c:forEach items="#{cur.entregaList}" var="c"> <c:if test="#{c.nota lt 6}"> <td class="table-danger">#{c.nota}</td> </c:if> <c:if test="#{c.nota ge 6}"> <td class="table-success">#{c.nota}</td> </c:if> </c:forEach> <c:if test="#{cur.missingNotaCount gt 0}"> <c:forEach begin="1" end="#{cur.missingNotaCount}"> <td class="table-danger">0.0</td> </c:forEach> </c:if> </tr> </c:forEach>
避坑提醒
- JSTL标签执行在视图构建阶段,JSF原生UI组件执行在视图渲染阶段,两个阶段的执行逻辑完全独立,不要试图靠代码书写顺序控制跨阶段的执行逻辑。
- EL表达式只适合做简单的字段取值和条件判断,任何涉及数据库查询、状态修改、数值计算的逻辑,全部放到后端实现,否则必然出现执行顺序混乱、重复调用、状态不一致的问题。
内容的提问来源于stack exchange,提问作者Sergio
相关产品推荐
相关产品推荐

