JSF 页面通过 UI 组件树进行展示,称作视图(view)。当客户端发起页面请求后,生命周期开始。在整个生命周期过程中,JSF 需要根据之前保存的状态来生成页面。当客户端提交页面的时候,JSF 必须执行几个任务,比如验证视图组件中的输入数据、转换客户端输入的数据为服务端定义的类型、绑定数据到后台 bean。JSF 执行这些任务是通过生命周期中的几个步骤。
不同的应用程序组件都需要经过以下生命周期:
恢复视图 (Restore View)
恢复和创建服务端组件树,用于展示客户端的 UI 信息。
如果客户端第一次请求到某个 URL,那么会创建一个新的 View 对象用于展示,这个视图会保存到当前的 FacesContext 中。如果已经在 FacesContext 找到视图对象,那么就会恢复视图并进行展示。
任何 UI 组件上的自定义转换器、验证器、渲染器也都会在这个时期被恢复。如果 UI 组件上的值直接映射到了后台 bean 的某个属性,那么属性值将会加载并且与 view 关联。大多数的这些操作都会 ViewHandler.restoreView 中进行。
在请求处理生命周期的恢复试图阶段,任何JSF的实现都必须执行下面的任务:
1 调用ViewHandler中的initView()方法。在这个方法中为请求设置相应的字符编码
2 检查当前请求中的FacesContext实例。如果FacesContext中已经包含了一个UIViewRoot对象,则: 从ExternalContext获得RequestLocale的值,并赋给UIViewRoot的locale 属性。
3 检查组件树中的每一个组件,如果组件中的”binding” 已经有ValueExpression,那就调用该ValueExpression的setValue() 方法。
4 根据如下算法判断该请求是回传的还是初始请求:通过调用Application ViewHandler的calculateRenderkitId()找到当前请求render-kit-id,然后再得到这个RenderKit的ResponseStateManager对象,调用isPostBack()方法。
5 对于初始化请求,必须调用FacesContext.renderResponse方法,以便可以跳过其他生命周期阶段,直接进入渲染响应阶段
6 对于回传请求,则调用ViewHandler.restoreView()方法,并返回一个UIViewRoot的对象。如果返回的UIViewRoot对象为空,则抛出ViewExpireException。然后再重新为其创建 一个组件树。
在这个阶段的结束时,FacesContext实例的viewRoot属性要么是从之前响应重新组装,要么是由ViewHandler.createView()方法创建一个新的view。
这个阶段的实现一定不能根据servlet mapping 去取得一个新的View识别符。言下之意就是说:要么创建一个新的view,要么从以前的响应中重组一个View。
应用请求值(Apply Requests)
这个时期会根据客户端提交的请求参数、header、cookie 来更新服务器端组件。
更准确地说,会调用所有组件的 UIComponent.processDecodes 方法。
在应用请求值的阶段, JSF的实现必须调用组件树中的UIViewRoot的processDecodes()方法,而这个方法也将会递归地调用组件树中所有组件的processDecodes()。而对于UIInput组件,这个时候还可能需要进行数据转换和验证。
在此阶段,某些组件还有一些特殊的行为:
- 实现ActionSource接口的组件会往事件队列里面添加一个ActionEvent。这些组件有:
如果该组件的属性immediate = true, 那么该事件将会在应用请求值阶段结束的时候被调用;否之,该事件将在调用应用程序阶段结束的时候才被调用。
- 实现EditableValueHolder接口,并且设置immediate = true的组件,这个时候将同时触发转换和验证流程,甚至是ValueChangeEvent事件。通常情况下,这些行为将在执行验证阶段发生的。实现这个接口的组有:
在这个阶段结束的时候,组件树里面所有实现EditableValueHolder的组件都将会更新为新的提交值,如果这些组件设置immediate = true,那么数据转换跟验证也会执行。如果数据转换或者验证出错,将会调用FacesContext的addMessage()方法。
处理验证 (Process Validations)
这个时期会处理 UIComponent 配置的验证和转换。
在这个时期,所有组件的 UIComponent.processValidators 方法将会被调用。如果验证和转换过程中出现错误,此生命周期将会结束并向客户端展示错误信息。
不管是被调用的任何validate()方法,还是执行事件队列里面的任何监听者,只要有调用到FacesContext中的responseComplete()方法,那么将会清空余下的所有事件,并且结束生命周期;而同样是上面的所有方法中,如果有人调用了FacesContext中的renderResponse()方法,则同样会清空余下的所有事件,并跳转到渲染响应阶段。正常的话将进入更新模型值阶段。
更新数据模型 (Update Model Values)
到这个时候,说明数据验证已经通过。
UIComponent 的值将会同步到模型对象,通常是后台 Bean。在这个时期,将会调用所有组件的 UIComponent.processUpdates 方法。设置模型对象的值有可能导致事件排队或触发。
调用应用 (Invoke Application)
调用业务逻辑和进行页面导航处理。
所有注册到 UIComponent 上的监听器将会被调用。例如,所有的像 command button 或 hyperlink 这样拥有默认事件监听器的 action 组件,它们的事件监听器将会在这个时期被调用。
JSF的实现必须确保UIViewRoot的processApplication()被调用。processApplication的默认行为是广播给那些指明阶段标识符为PhaseId.INVOKE_APPLICATION的事件。
一些高级的应用程序,或者说应用框架一般会通过调用setActionListner方法来取代默认的ActionListerner。不管怎样,JSF的实现都必须提供一个默认的ActionListerner
渲染响应 (Render Response)
渲染响应到客户端。
在渲染响应之前,应用程序会自动调用 View 的 UIViewRoot.saveState 方法保存状态。
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的方法进行保存。这些状态信息必须在后面的请求是可访问的,这样,恢复视图阶段才能够读取他们。
参考:http://www.aptusource.org/2014/04/java-ee-7-request-processing-life-cycle-phases/
https://www.sogou.com/link?url=DOb0bgH2eKh1ibpaMGjuyzKVWEC5ux0EI72idEXAoIquSdKpCMIsLu1QM0fDTYbbgUbYskUotWg.