日期:2014-05-16  浏览次数:20372 次

JSF 技术内幕

当浏览器首先连接到http://localhost:8080/faces/index.xhtml时,JSF实现初始化JSF代码并读取index.xhtml页面。这个页面包含诸如h:inputText等JSF标签。每个标签都有一个相关的标签处理程序类。当读取该页面时,执行相应的标签处理程序。JSF标签处理程序彼此协作来构建一棵组件树。

组件树是一种数据结构,其中包含JSF页面上所有用户界面元素的Java对象。例如UIInput对象分别对应于JSF文件中的h:inputText和h:inputSecret字段。

呈现页面

接着呈现HTML页面。除JSF标签外的所有文本直接显示。h:form、h:inputText、h:inputSecret和h:commandButton标签则被转换为HTML。其中每个标签都调用相关的组件。每个组件有一个呈现器以生成HTML输出,反映组件状态。例如,与h:inputText标签对应的组件呈现器生成以下输出:

<input type="text" name="unique ID" value="current value"/>

这个进程称为编码。UIInput对象的呈现器要求JSF实现查找user.name表达式的唯一ID和当前值。默认情况下,由JSF实现指定ID字符串。ID看上去是随机的。

编码页被发送到浏览器,浏览器按通常的方式显示它。

请求解码

当在浏览器中显示页面后,用户填写表单字段并单击登录按钮。浏览器将表单数据发回到Web服务器,格式化为一个POST请求。这是一种特殊格式,并被定义为HTML协议的一部份。POST请求包含表单的URL(/faces/index.xhtml)以及表单数据。

注意:POST请求的URL与呈现表单的请求的URL一样。在提交表单后,将导航到新页面(由于这个原因,浏览器中显示的URL通常比所显示JSF页面的URL晚一步)。

表单数据是ID/值对形式的字符串,使用“&”连接多个键。作为正常请求处理的一部份,表单数据位于所有组件都可以访问的Hash表中。

接下来,JSF实现为每个组件提供了一个检查Hash表的机会,这个过程称为解码。每个组件自行决定如何解释表单数据。

登录表单有三个组件对象:两个UIInput对象(与表单的文本字段对应)和一个UICommand对象(与提交按钮对应)。

  • UIInput组件更新value特性中引用的bean属性:它们使用用户提供的值调用setter方法。
  • UICommand组件检查用户是否单击了按钮。如果单击了,它触发动作事件来启动action特性引用的login操作。这个事件告诉导航处理程序查找后续页面welcome.xhtml。

然后重复以上步骤。

JSF实现的两个最重要的处理步骤:编码和解码。但是,处理顺序(也称为生命周期)则较为复杂。如果一切正常,那么用户则不必担心生命周期的复杂性。然而,当发生错误时,就必须了解JSF实现做了什么。

生命周期

JSF规范定义了六个不同的阶段:

  • 还原视图
  • 应用请求值
  • 过程验证(处理验证)
  • 更新模型值
  • 调用应用程序
  • 呈现响应

这里讲述生命周期最常见的流程:

  1. 如果请求的页面曾在以前显示过,“还原视图”阶段将检索请求页面的组件树;如果请求页面首次显示,“还原视图”将创建组件新的组件树。
  2. 如果没有请求值,JSF实现会直接跳转到前面的“呈现响应”阶段。这发生在页面第一次显示之时。否则进入(3)。
  3. 如果有请求值,则进入“应用请求值”阶段。在此阶段,JSF实现迭代组件树中的每个组件对象。每个组件对象都检查哪些请求值属于自己并将其存储。存储在组件中的值称为“本地值”。当设计JSF页面时,可添加验证器以验证本地值的正确性。这些验证器在“处理验证”阶段执行。如果通过验证,JSF生命周期正常进行。但当发生转换或验证错误时,JSF实现会直接调用“呈现响应”阶段,重新显示当前页以便用户有机会再次提供正确输入。
    注意:如果转换器或验证器失败,只是重新显示当前页。应该添加标签来显示验证错误,以便让用户知道为什么他们又一次看到了以前的页面。
  4. 在转换器和验证器完成工作后,即认为可以安全地更新模型数据。在“更新模型值”阶段,会使用本地值来更新与组件关联的bean。
  5. 在“调用应用程序”阶段,引起表单提交的按钮或链接组件的action方法被执行。该方法可以执行任意的应用程序处理。它返回的结果字符串传给导航处理程序。导航处理程序然后查找下一个页面。
  6. 最后,“呈现响应”阶段对响应进行编码,并将它发回浏览器。当用户提交表单、单击链接或者生成新请求时,这个循环重新开始。

注意:在Ajax应用中,Ajax请求将输入组件添加到execute列表中,将输出组件添加到render列表中。对execute列表中的组件来说,会执行除“呈现响应”外的所有阶段。确切地讲,在“更新模型值”阶段,会更新模型bean。与此相反,对于render列表中的组件来说,仅执行生命周期的“呈现响应”阶段,结果被发回到Ajax请求。