在jsf中,一旦session过期,生命周期为session的Controller bean对象将会被销毁,因此除了一些静态页面,大多数情况的动态页面视图也就无法恢复。如果没有在web.xml设置error page,那么系统会直接在页面中抛出异常。当然关掉页面重新打开就不会有这个问题,但是普通浏览者这个时候往往会不知所措,以为网站挂掉了,这样可用性会很差。
一般网上提供的解决方法是在web.xml中设置error page来捕获错误,重定向到一个指定的页面,例如:
<error-page>
<exception-type>javax.faces.application.ViewExpiredException</exception-type>
<location>/login.jsf</location>
</error-page>
但悲惨的是,在调试中发现javax.faces.application.ViewExpiredException这个异常并不会被捕获到,com.sun.faces.lifecycle.LifecycleImpl的execute方法抛出的实际上是FacesException。我怀疑可能是jsf的2.0版本修改了这一块,而网上的解决方法是针对老版本的。
如此,我们只能通过从com.sun.faces.lifecycle.LifecycleImpl派生一个类来处理这个异常,代码如下:
import javax.faces.FacesException;
import javax.faces.application.ViewExpiredException;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.ppedu.commons.SystemProperty;
import com.sun.faces.lifecycle.LifecycleImpl;
public class TeleLifecycleImpl extends LifecycleImpl {
public TeleLifecycleImpl() {
super();
}
public void execute(FacesContext context) {
try {
super.execute(context);
}
catch (ViewExpiredException vee) {
//似乎永远不会捕获,应该是异常在父类的同名方法中被包装成了FacesException
redirect(context);
}
catch (FacesException fe) {
//取得该异常的root cause
Object obj= fe.getCause();
//判断是否ViewExpiredException
if (obj instanceof ViewExpiredException){
redirect(context);
}
else{
throw fe;
}
}
}
private void redirect(FacesContext context) {
try {
context.responseComplete();
context.renderResponse();
HttpServletResponse response = (HttpServletResponse) context.getExternalContext().getResponse();
//根据原来的请求路径确定要重定向的路径
HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
String path = request.getRequestURI();
String [] ss = path.split("/");
String redirectKey = "session.timeout.redirect.url.general";
for(int i=0;i<ss.length;i++){
if( ss[i].equals("security") || ss[i].equals("organManagementCenter") || ss[i].equals("webManageCenter")){
//管理模块
redirectKey = "session.timeout.redirect.url.management";
break;
}
if( ss[i].equals("usercenter")){
//个人中心
redirectKey = "session.timeout.redirect.url.usercenter";
break;
}
}
//目标页面
String url = context.getExternalContext().getRequestContextPath() + SystemProperty.getKeyValue(redirectKey);
response.sendRedirect(url);
} catch (Exception e) {
System.out.println("url redirect wrong ");
}
}
}
这个类可以捕获ViewExpiredException引起的异常,将页面重定向到实现设定的url。com.ppedu.commons.SystemProperty是一个读配置文件的类,读者可以自己实现一个,配置文件设置了三个url,分别指向网站首页、用户中心登陆页和管理中心登录页,设置如下:
#============================================================================
# session过期后的默认页
#============================================================================
session.timeout.redirect.url.general = /home.jsf
session.timeout.redirect.url.management = /security/managementLogin.jsf
session.timeout.redirect.url.usercenter = /usercenter/login.jsf
然后,我们创建一个工厂类:
import com.sun.faces.lifecycle.LifecycleFactoryImpl;
public class TeleLifecycleFactoryImpl extends LifecycleFactoryImpl {
public final static String CUSTOM_LIFECYCLE = "TELEEPOCH";
/** Creates a new instance of TeleLifecycleFactoryImpl */
public TeleLifecycleFactoryImpl() {
addLifecycle(CUSTOM_LIFECYCLE,new TeleLifecycleImpl());
}
}
接着,在faces-config.xml中注册这个工厂类来监查生命周期:
<factory>
<lifecycle-factory>com.ppedu.view.TeleLifecycleFactoryImpl</lifecycle-factory>
</factory>
OK,搞定收工,测试一下看看是否能获得预期效果。
JSF中session过期后视图恢复的问题
最新推荐文章于 2017-07-03 15:53:29 发布