在JSF宏观的生命周期中分为Execute和Render,在Execute部分又将分为具体的步骤(可参考前面博客中的流程图)一共是六个步骤在后面的文章中笔者将带着大家慢慢的走进JSF(JavaEE仅仅是给了一个规范,至于具体代码如何实现。不同的中间件会有所不同),本篇博客就详细介绍第一阶段Restore View Phase。
道德经里说“道生一,一生二,二生三,三生万物。”任何复杂的过程都将从简单开始,对于JSF也不例外,JSF所作的一切动作都是一个请求引起的。通常情况下这个请求的触发往往是因为用户点击链接或者是单击了一个提交按钮,当请求到达服务器JSF就开始了这个步骤(Restore View Phase)。可能刚刚接触JSF声明周期的人不理解为什么第一个阶段就是Restore(可以翻译为重建,恢复)?这就要联系前面JSF把请求分为两类来说了。对于JSF而言请求不是initial就是postback,所以当一个请求到达的时候JSF默认为此请求所对应的view已经被存储了,所以要做的事情就是Restore这个view。如果这个view没有被储存过(是一个全新的view),那很简单直接创建并存储就可以了。这个过程类似于乐观锁,相信一切都是美好的,至于说在享受美好的过程中遇到了问题我们解决它然后重新回归美好就是了。
RestoreView Phase核心execute方法(jsf-impl-2.1.3,RestoreViewPhase.java)
public void execute(FacesContext facesContext) throws FacesException {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine("Entering RestoreViewPhase");
}
if (null == facesContext) {
throw new FacesException(MessageUtils.getExceptionMessageString(
MessageUtils.NULL_CONTEXT_ERROR_MESSAGE_ID));
}
// If an app had explicitely set the tree in the context, use that;
//
UIViewRoot viewRoot = facesContext.getViewRoot();
if (viewRoot != null) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine("Found a pre created view in FacesContext");
}
facesContext.getViewRoot().setLocale(
facesContext.getExternalContext().getRequestLocale());
// do per-component actions
deliverPostRestoreStateEvent(facesContext);
if (!facesContext.isPostback()) {
facesContext.renderResponse();
}
return;
}
FacesException thrownException = null;
try {
// Reconstitute or create the request tree
Map requestMap = facesContext.getExternalContext().getRequestMap();
String viewId = (String)
requestMap.get("javax.servlet.include.path_info");
if (viewId == null) {
viewId = facesContext.getExternalContext().getRequestPathInfo();
}
// It could be that this request was mapped using
// a prefix mapping in which case there would be no
// path_info. Query the servlet path.
if (viewId == null) {
viewId = (String)
requestMap.get("javax.servlet.include.servlet_path");
}
if (viewId == null) {
viewId = facesContext.getExternalContext().getRequestServletPath();
}
if (viewId == null) {
throw new FacesException(MessageUtils.getExceptionMessageString(
MessageUtils.NULL_REQUEST_VIEW_ERROR_MESSAGE_ID));
}
ViewHandler viewHandler = Util.getViewHandler(facesContext);
boolean isPostBack = (facesContext.isPostback() && !isErrorPage(facesContext));
if (isPostBack) {
facesContext.setProcessingEvents(false);
// try to restore the view
viewRoot = viewHandler.restoreView(facesContext, viewId);
if (viewRoot == null) {
if (is11CompatEnabled(facesContext)) {
// 1.1 -> create a new view and flag that the response should
// be immediately rendered
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine("Postback: recreating a view for " + viewId);
}
viewRoot = viewHandler.createView(facesContext, viewId);
facesContext.renderResponse();
} else {
Object[] params = {viewId};
throw new ViewExpiredException(
MessageUtils.getExceptionMessageString(
MessageUtils.RESTORE_VIEW_ERROR_MESSAGE_ID,
params),
viewId);
}
}
facesContext.setViewRoot(viewRoot);
facesContext.setProcessingEvents(true);
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine("Postback: restored view for " + viewId);
}
} else {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine("New request: creating a view for " + viewId);
}
String derivedViewId = viewHandler.deriveLogicalViewId(facesContext, viewId);
ViewDeclarationLanguage vdl = viewHandler.getViewDeclarationLanguage(facesContext, derivedViewId);
if (vdl != null) {
// If we have one, get the ViewMetadata...
ViewMetadata metadata = vdl.getViewMetadata(facesContext, viewId);
if (metadata != null) { // perhaps it's not supported
// and use it to create the ViewRoot. This will have, at most
// the UIViewRoot and its metadata facet.
viewRoot = metadata.createMetadataView(facesContext);
// Only skip to render response if there are no view parameters
Collection<UIViewParameter> params =
ViewMetadata.getViewParameters(viewRoot);
if (params.isEmpty()) {
facesContext.renderResponse();
}
}
} else {
facesContext.renderResponse();
}
if (null == viewRoot) {
viewRoot = (Util.getViewHandler(facesContext)).
createView(facesContext, viewId);
}
facesContext.setViewRoot(viewRoot);
assert (null != viewRoot);
}
} catch (FacesException fe) {
thrownException = fe;
} finally {
if (null == thrownException) {
deliverPostRestoreStateEvent(facesContext);
} else {
throw thrownException;
}
}
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine("Exiting RestoreViewPhase");
}
}
FacesContext当中包含了处理一个请求所有的信息。所有程序的组件,事件处理器,转化器,校验器都会放置在FacesContext的实例当中。所以在这个步骤中除了会建立并保存相应的view之外,还会将事件处理器以及校验期提供给view当中的组件,为用户后续的操作做准备。
对于JSF来说如果请求的是initial类型的请求(第一次),那么JSF会创建一个空的view并且生命周期会直接进行到Render Response这一步(从代码98行开始),在此过程中空的view会和要显示的页面中引用组件的tag放在一起(其实这句话没有懂哇!)。
如果请求是postbacks方式的请求,对应的这个view已经在FacesContext实例当中,在此步骤中JSF实现者会根据客户端或者服务器的状态信息恢复这个view(代码85行),这里笔者有个问题一直没有在官方文档中找到答案,代码65行在判断这个View是否为null,问题是如果是postback类型的请求为什么View还会是null呢?而且在下面的代码中重新创建了一个新的view,不过笔者根据方法名字(is11CompatEnabled)猜测这可能和兼容性有关。
在整个Restore View阶段,核心的宗旨就是不同的请求区别对待:initial-->创建+保存;postback-->恢复。