1. 恢复视图
在请求处理生命周期的恢复试图阶段,任何JSF的实现都必须执行下面的任务:
l 调用ViewHandler中的initView()方法。在这个方法中为请求设置相应的字符编码
l 检查当前请求中的FacesContext实例。如果FacesContext中已经包含了一个UIViewRoot对象,则:
n 从ExternalContext获得RequestLocale的值,并赋给UIViewRoot的locale属性。
n 检查组件树中的每一个组件,如果组件中的”binding” 已经有ValueExpression,那就调用该ValueExpression的setValue() 方法。
l 根据如下算法判断该请求是回传的还是初始请求:通过调用Application ViewHandler的calculateRenderkitId()找到当前请求render-kit-id,然后再得到这个RenderKit的ResponseStateManager对象,调用isPostBack()方法。
l 对于初始化请求,必须调用FacesContext.renderResponse方法,以便可以跳过其他生命周期阶段,直接进入渲染响应阶段
l 对于回传请求,则调用ViewHandler.restoreView()方法,并返回一个UIViewRoot的对象。 如果返回的UIViewRoot对象为空,则抛出ViewExpireException。然后再重新为其创建一个组件树。
在这个阶段的结束时,FacesContext实例的viewRoot属性要么是从之前响应重新组装,要么是由ViewHandler.createView()方法创建一个新的view。
这个阶段的实现一定不能根据servlet mapping 去取得一个新的View识别符。言下之意就是说:要么创建一个新的view,要么从以前的响应中重组一个View。(自己说)
MyFaces:
Myfaces的实现中,当组件树渲染完成后,会将组件树进行编码,存放在一个叫” jsf_tree_64”的隐藏字段传回前台。当回传时,在恢复视图阶段,根据这个字段来重装组件树。
2. 应用请求值
应用请求值阶段的目的是:根据当前请求中(参数值,请求头,cookies等)的值,更新组件树中每一个组件的当前状态。
在应用请求值的阶段, JSF的实现必须调用组件树中的UIViewRoot的processDecodes()方法,而这个方法也将会递归地调用组件树中所有组件的processDecodes()。而对于UIInput组件,这个时候还可能需要进行数据转换和验证。
在此阶段,某些组件还有一些特殊的行为:
- 实现ActionSource接口的组件会往事件队列里面添加一个ActionEvent。这些组件有:
如果该组件的属性immediate = true, 那么该事件将会在应用请求值阶段结束的时候被调用; 否之,该事件将在调用应用程序阶段结束的时候才被调用。
- 实现EditableValueHolder接口,并且设置immediate = true的组件,这个时候将同时触发转换和验证流程,甚至是ValueChangeEvent事件。通常情况下,这些行为将在执行验证阶段发生的。实现这个接口的组有:
在这个阶段结束的时候,组件树里面所有实现EditableValueHolder的组件都将会更新为新的提交值, 如果这些组件设置immediate = true,那么数据转换跟验证也会执行。如果数据转换或者验证出错,将会调用FacesContext的addMessage()方法。
不管是被调用的任何decode()方法,还是执行事件队列里面的任何监听者,只要有调用到FacesContext中的responseComplete()方法,那么将会清空余下的所有事件,并且结束生命周期; 而同样是上面的所有方法中,如果有人调用了FacesContext中的renderResponse()方法,则同样会清空余下的所有事件,并跳转到渲染响应阶段。
MyFaces:
MyFaces的实现中,在每个组件的decode()方法中,首先是获取该组件对应的Renderer,再调用该Renderer的decode()方法。
如果一个组件有注册了响应的事件,那么在这个阶段就会创建并初始化响应的事件属性。
3. 执行验证
在执行验证的阶段中,JSF的实现必须调用UIViewRoot的processValidators()方法。同样,这个方法将会递归调用所有组件的processValidator方法。需要注意的是,那些实现EditableValueHolder接口,并且设置immediate=true的组件已经在 应用请求值阶段执行了数据转换和验证工作,这里不用再做。
当这个阶段结束时,所有的转换和配置的验证工作都将完成。
不管是被调用的任何validate()方法,还是执行事件队列里面的任何监听者,只要有调用到FacesContext中的responseComplete()方法,那么将会清空余下的所有事件,并且结束生命周期; 而同样是上面的所有方法中,如果有人调用了FacesContext中的renderResponse()方法,则同样会清空余下的所有事件,并跳转到渲染响应阶段。正常的话将进入更新模型值阶段。
MyFaces:
Myfaces中,还是通过组件对应的Renderer来进行,调用getConvertValue的方法进行转换。如果没有指定的Renderer,则默认转换成String。
4. 更新模型值
如果一个请求能够走到这,说明该请求从语义上、语法上是合法的,每个组件也都更新到请求值。这时也就该去更新对应的模型值,为执行队列里的应用程序事件做准备。
在更新模型值阶段,JSF的实现必须调用UIViewRoot的processUpdate()方法,老规则,这将递归调用组件树里面所有的processUpdate()方法。一般来说,都是有组件的updateModel()方法来完成的。
MyFaces1.1.4里面的UIInput的updateModel大致如下:
public void updateModel(FacesContext context)
{
if (!isValid()) return;
if (!isLocalValueSet()) return;
ValueBinding vb = getValueBinding("value");
if (vb == null) return;
try
{
vb.setValue(context, getLocalValue());
setValue(null);
setLocalValueSet(false);
}
}
在这个阶段结束的时候,所有适当的模型数据对象将会跟对应的组件对接上,而该组件上的值也将被清空。
不管是被调用的任何updateModel()方法,还是执行事件队列里面的任何监听者,只要有调用到FacesContext中的responseComplete()方法,那么将会清空余下的所有事件,并且结束生命周期; 而同样是上面的所有方法中,如果有人调用了FacesContext中的renderResponse()方法,则同样会清空余下的所有事件,并跳转到渲染响应阶段。正常的话将进入更新模型值阶段。
MyFaces:
Myfaces在这个阶段的实现是:获取或者创建对应的ValueBinding,然后赋值,清空组件上的Local Value
5. 调用应用程序
JSF的实现必须确保UIViewRoot的processApplication()被调用。processApplication的默认行为是广播给那些指明阶段标识符为PhaseId.INVOKE_APPLICATION的事件。
一些高级的应用程序,或者说应用框架一般会通过调用setActionListner方法来取代默认的ActionListerner。不管怎样,JSF的实现都必须提供一个默认的ActionListerner
MyFaces:
Myfaces的实现中,首先是广播给所有在应用请求值阶段注册的事件。而在这些事件中,包含了对应的UIComponent,然后这些UIComponent再广播给所有的监听者。如果该组件是UICommand的话,还会调NavigationHandler进行导航。如果找到对应的NavigationRule,则重新创建一个UIViewRoot,并赋给FacesContext实例。
6. 渲染响应
这个阶段要完成两件事:
- 把响应渲染到客户端
- 保存响应的内容,以便接下来的请求可以处理
把这两件事绑定到这个阶段的理由是:在一个JSP的应用中,渲染响应就需要创建view,而我们需要在view创建之后才能够保存状态,并且我们不得不在发送响应给用户之前把状态也保存到客户端去。
JSF规范为JSF的实现提供了很多中创建响应内容的方式,包括:
- 直接通过调用组件的encoding方法获得所有的响应内容
- 结合应用程序的编程逻辑,把动态产生的组件编码结果插入到响应内容
- 把静态模板跟组件编码结果结合起来,产生响应内容
- 结合嵌入的方法,把编码结果插入到动态资源(比如把组件JSP自定义标签)
由于实现的方式很多,所以也不明确指出实现的机制应该怎样。但是,所有JSF的实现在这个阶段都必须实现下面的需求:
- JSF的实现必需提供一个默认的ViewHandler,该实现能够通过调用RequestDispatcher.forward()方法来获得组件树View标识符所对应的上下文路径。
- 如果所有的响应内容都由组件或者响应的渲染器完成,那么组件树应该能够像前面阶段一样,以深度优先的算法遍历组件树。
- 如果渲染内容是由外部资源跟编码方法一起完成的话,那么组件应该可以选择任何渲染的优先顺序
- 在渲染过程中,基于ViewHandler实现提供的有效信息,外部资源可以添到组件树里面。但是,在添加新组件之前,ViewHandler实现要先检查组件树里面是否有存在的组件。如果存在的话,该组件的属性必须是可使用的
- 当一个组件的父组件、或者任何父辈的组件设置rendersChilderen=true时,这个组件在任何情况都不能进行渲染。在这个情况下,其父组件或者父辈组件在被渲染的时候,一定要渲染这些子组件
- 当一个组件的isRendered()方法返回false值,该组件的渲染器不能产生任何的标记,包括facets和子组件
- 在任何请求生命周期中(除了渲染阶段),都应该允许应用程序修改组件树,并使系统产生预期行为。 比如,下面的行为必须是允许的:
- 在渲染阶段修改View可能导致不可预期的结果
- 允许在渲染之前,将通过模板系统(比如JSP)添加到组件树中的组件移除。
- 允许以编程的方式添加组件到组件树中,并且能够在恰当的层次结构中渲染它
- 允许在渲染之前,重新对组件树进行排序
- 任何添加到组件树中的组件的ID都必须在最近的父命名控件范围里面是惟一
- rendersChildren属性的值必须为true 或者false
- 当组件树中每个独立的组件进行渲染时,调用其encodeXXX()方法进行编码。
在渲染接近完成时,view 的完成状态必须使用类StateManager的方法进行保存。这些状态信息必须在后面的请求是可访问的,这样,恢复视图阶段才能够读取他们。
MyFaces:
MyFaces的实现中,默认的ViewHandler是JspViewHandler,采用的方式应该是把静态的模板跟组件编码结果结合起来做渲染的方式。