这是因为它是按照 JSF 规范ViewExpiredException
包装在 a中的。这是JSF 1.2 规范ServletException
第 10.2.6.2 章的摘录:
10.2.6.2 FacesServlet
调用已execute()
保存实例的方法,将此请求的实例作为参数Lifecycle
传递
。FacesContext
如果该execute()
方法抛出 a FacesException
,则将其作为 a 重新抛出,并ServletException
作为
FacesException
根本原因。
Servlet API 规范中指定了如何分配错误页面。这是Servlet API 规范 2.5第 9.9.2 章的摘录:
SRV.9.9.2 错误页面
如果没有 error-page
包含exception-type
使用
类层次结构 match的声明,并且抛出的异常是 aServletException
或其子类,则容器提取ServletException.getRootCause
方法定义的包装异常。对错误页面声明进行第二次传递,再次尝试匹配错误页面声明,但使用包装的异常。
在类层次结构中,ServletException
已经匹配Throwable
,因此不会为第二遍提取其根本原因。
要证明此指定行为,请替换javax.faces.application.ViewExpiredException
为javax.servlet.ServletException
as<exception-type>
并重试。您将看到正在显示的预期错误页面。
要解决此问题,只需删除java.lang.Throwable
或上的错误页面java.lang.Exception
。如果没有一个异常特定的错误页面匹配,那么它将回退到错误代码的那个500
。所以,你只需要这样:
<error-page>
<exception-type>javax.faces.application.ViewExpiredException</exception-type>
<location>/jsps/utility/sessionExpired.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/jsps/utility/technicalError.jsp</location>
</error-page>
更新:根据 OP 的(已删除)注释:要可靠地测试这一点,您不能throw new ViewExpiredException()
在 bean 构造函数或方法中执行。反过来,它会被包裹在一些 EL 异常中。您最终可以在 中添加调试行打印rootCause
以Filter
自己查看。
如果您使用的是 Eclipse/Tomcat,一个快速的测试ViewExpiredException
方法如下:
- 使用简单的命令按钮创建一个 JSF 页面,部署并运行它并在 webbrowser 中打开它。
- 返回 Eclipse,右键单击 Tomcat 服务器并选择Clean Tomcat Work Directory。这将重新启动 Tomcat并丢弃所有序列化会话(重要!仅重新启动 Tomcat 是不够的)。
- 返回网络浏览器并按下命令按钮(无需事先重新加载页面!)。