如何阻止JSF1.2重复加载页面:多标签页提交异常问题
这确实是JSF 1.2在多标签页场景下很常见的视图状态冲突问题,咱们结合你的代码和场景一步步拆解:
问题根源分析
JSF 1.2视图状态的会话存储特性
JSF 1.2默认会把视图状态(ViewState)存储在HttpSession中,每个打开的视图都会在会话里留下对应的状态对象。当你打开多个标签页时,会话中会堆积多个同类型视图的状态数据。提交其中一个标签页后,虽然导航到了目标页面,但如果后续有隐式请求(比如过滤器逻辑、页面加载时的后台操作),很可能错误地从会话中取出另一个标签页的视图状态,导致页面回显了错误的数据。Request Scope Bean的会话依赖隐患
你的testbean是request scope,但初始化时直接从会话全局获取controller实例。当多个标签页都往会话里存入了不同的controller数据,新创建的request Bean实例拿到的会是最后存入会话的那个controller(也就是标签页2的)。这就解释了为什么提交标签页1后,页面又跳回标签页1却显示标签页2的数据——新的request Bean初始化时,拿到了会话里残留的标签页2的controller数据。会话数据清理不及时
你提到提交的JSF页面会关闭会话,但实际操作中可能会话并没有被正确销毁,或者视图状态、controller数据没有从会话中彻底移除,导致后续请求还能访问到旧的标签页数据。
针对性解决方案
1. 隔离多标签页的视图状态(最彻底的方案)
把JSF的视图状态存储方式从会话改为客户端,这样每个标签页的视图状态会存在自身表单的隐藏域中,完全独立不会互相干扰。在web.xml中添加以下配置:
<context-param> <param-name>javax.faces.STATE_SAVING_METHOD</param-name> <param-value>client</param-value> </context-param>
注意:客户端存储模式会把视图状态序列化后存在表单的
javax.faces.ViewState隐藏域中,如果视图数据量大,可能会影响请求性能,但能彻底解决多标签页的会话视图冲突问题。
2. 给会话数据添加标签页唯一标识
不要在会话中存储全局的controller,而是给每个标签页分配一个唯一的tabId,用这个ID作为会话Map的key来存储对应标签页的controller数据,避免互相覆盖。
修改testbean的初始化方法:
public class testbean { public testbean() { initilize(); } private void initilize() { FacesContext facesContext = FacesContext.getCurrentInstance(); Map<String, Object> session = facesContext.getExternalContext().getSessionMap(); // 从请求参数获取当前标签页的唯一ID(打开标签页时传入) String tabId = facesContext.getExternalContext().getRequestParameterMap().get("tabId"); if (tabId != null) { // 按tabId从会话获取对应controller controller = (ControllerType) session.get("controller_" + tabId); } else { // 兼容单个标签页的默认情况 controller = getDataFromSession(session); } } }
打开标签页时,要给每个标签页的请求传入唯一的tabId参数,比如:
- 标签页1的链接:
/test.jsf?tabId=tab_20240520_001 - 标签页2的链接:
/test.jsf?tabId=tab_20240520_002
3. 提交后主动清理会话数据
在submit方法处理完业务后,根据当前标签页的tabId,主动移除会话中对应的controller数据,避免残留数据干扰后续请求:
public String submit() { // 执行你的业务逻辑... // 获取当前标签页ID并清理会话数据 FacesContext facesContext = FacesContext.getCurrentInstance(); String tabId = facesContext.getExternalContext().getRequestParameterMap().get("tabId"); if (tabId != null) { facesContext.getExternalContext().getSessionMap().remove("controller_" + tabId); } // 确保会话被正确销毁(如果业务需要) facesContext.getExternalContext().invalidateSession(); // 返回导航结果 return "Page4"; }
4. 优化导航规则的准确性
检查你的导航规则,确保from-action和from-outcome的匹配没有歧义。如果不同标签页的提交需要导航到不同页面,可以给每个标签页的submit逻辑设置不同的outcome,或者添加更明确的导航条件,避免意外跳转。
内容的提问来源于stack exchange,提问作者Java_Alert

