在这个过程当中JSF的实现者使用processValidators方法处理所有在tree中的组件中注册的验证器。验证的过程就是通过每个组件已有的规则对其已经保存的值进行校验,同时也对输入的值进行校验,前提是组件的immediate属性没有设置为true。从代码来看在UIViewRoot中的这个processValidators方法和上个阶段中的processDecodes基本一致,不用说下一个阶段(Update ModelValues Phase)也会有相类似的方法(processUpdates)。之所以JSF会这样设计是因为这三个阶段(取值、校验、更新)所作的事情对于每个组件(一个View的各个部分)而言是一致的。
PartialViewContextImpl中经典的一个方法,此方法将“处理”抽象了出来,妙不可言哇。
public void processPartial(PhaseId phaseId) { updateFacesContext(); PartialViewContext pvc = ctx.getPartialViewContext(); Collection <String> executeIds = pvc.getExecuteIds(); Collection <String> renderIds = pvc.getRenderIds(); UIViewRoot viewRoot = ctx.getViewRoot(); if (phaseId == PhaseId.APPLY_REQUEST_VALUES || phaseId == PhaseId.PROCESS_VALIDATIONS || phaseId == PhaseId.UPDATE_MODEL_VALUES) { // Skip this processing if "none" is specified in the render list, // or there were no execute phase client ids. if (executeIds == null || executeIds.isEmpty()) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "No execute and render identifiers specified. Skipping component processing."); } return; } try { processComponents(viewRoot, phaseId, executeIds, ctx); } catch (Exception e) { if (LOGGER.isLoggable(Level.INFO)) { LOGGER.log(Level.INFO, e.toString(), e); } } // If we have just finished APPLY_REQUEST_VALUES phase, install the // partial response writer. We want to make sure that any content // or errors generated in the other phases are written using the // partial response writer. // if (phaseId == PhaseId.APPLY_REQUEST_VALUES) { PartialResponseWriter writer = pvc.getPartialResponseWriter(); ctx.setResponseWriter(writer); } } else if (phaseId == PhaseId.RENDER_RESPONSE) { try { // // We re-enable response writing. // PartialResponseWriter writer = pvc.getPartialResponseWriter(); ResponseWriter orig = ctx.getResponseWriter(); ctx.getAttributes().put(ORIGINAL_WRITER, orig); ctx.setResponseWriter(writer); ExternalContext exContext = ctx.getExternalContext(); exContext.setResponseContentType("text/xml"); exContext.addResponseHeader("Cache-Control", "no-cache"); writer.startDocument(); if (isRenderAll()) { renderAll(ctx, viewRoot); renderState(ctx); writer.endDocument(); return; } // Skip this processing if "none" is specified in the render list, // or there were no render phase client ids. if (renderIds == null || renderIds.isEmpty()) { } else { processComponents(viewRoot, phaseId, renderIds, ctx); } renderState(ctx); writer.endDocument(); } catch (IOException ex) { this.cleanupAfterView(); } catch (RuntimeException ex) { this.cleanupAfterView(); // Throw the exception throw ex; } } }
如果本地值不合法,或者任何转换失败那么JSF的实现将会在FacesContext实例中增加一条错误信息,然后生命周期直接跳转到Render Response阶段页面会被再次渲染并且附带上刚才的错误信息。当然之前产生的放在FacesContext中的错误信息也会一并显示出来。
如果在当前FacesContext实例中任何validate方法或者事件监听器调用renderResponse方法JSF实现将跳到Render Response阶段。
和上个阶段一样如果程序需要重定向到不同web应用资源或者生成一个不包含JSF组件的响应那么会调用FacesContext.responseComplete方法。
如果当前请求被定义为一个局部的请求,那么局部内容会被从FacesContext中回复,而且局部处理方法会被执行。
细心的读者可能看出来了后面的三种情况和上一步骤中的流程是惊人的相似,这归根结底是因为JSF实现者人为的将Execute步骤分成了6个不同的阶段,但是这六个阶段中的中间三个( Apply Request Values Phase、Process Validations Phase、Update Model Values Phase)阶段从本质上来看是一模一样的。这三个阶段都是为组件中值的展示服务的,不论是取值,还是校验,还是更新他们都是相同得逻辑不同的细节,这也就能解释了为什么三个阶段可以公用上面贴出的一套代码而只用传入不同的PhaseId。哦,策略模式!好像又不太像,这里仅仅是传递不同的参数然后不同的操作而已。工厂?有些类似,但是不完全是,工厂最起码得有创建吧。想了一圈设计模式也不知道哪个能和这里的实现相对应,总之这里抽象了,复用性高了,耦合性恰到好处。