将 StrutsTiles 和 JavaServer Faces (JSF) 一起使用开发人员可以实现易于管理和重用的健壮的界面清晰的 Web 应用程序
Struts 框架推出已经有一段时间了它已经成为在开发 JEE Web 应用程序时开发人员所采用的事实上的标准Tiles 框架是在 Struts 之后不久出现的它通过为开发人员提供用组件组装展示页面的能力开拓了自己的生存环境JSF 是 Web 应用程序框架中最新的成员它提供了验证用户输入和处理用户事件的机制最重要的是这是一种以协议无关的方式呈现用户界面组件的方法(有关这些 技术的概况参见本文相关页面The major players)
尽管 Struts 和 JSF 中有一些功能是重叠的但是它们在其他方面起到了互为补充的作用这三种技术的结合可以为开发 Web 应用程序组织其展示和以协议无关的方式呈现定制的用户界面(UI)组件提供一种高效的途径
为了运行本文中的示例代码需要 Struts TilesJavaServer Faces Reference Implementation (JSFRI) Early Access Release 以及 StrutsFaces Jakarta 项目提供的 Struts 发行版本将 Struts 和 Tiles 捆绑发布还可以从 Jakarta 项目上下载 StrutsFaces 集成库JSFRI 是 Sun 的 Web 开发工具包(Web Services Developer Pack)的一部分(在参考资料中有这些下载和示例代码的链接)
现在回到集成三种技术的细节上首先有个坏消息在本文发表的时候这三种技术是不能直接互操作的好消息是在本文中我们展示了集成 StrutsTiles 和 JSF 的方法我们假设您已经了解 Struts 和 Tiles对 JSF 有一些了解会有帮助(参阅 参考资料中提供的 developerWorks 上的 JSF 教程的链接)但是不了解也不妨碍对本文的理解
JSF 简介
JSF 应用程序是使用 JSF 框架的普通 JEE Web 应用程序JSF 框架提供了丰富的 GUI 组件模型这些模型体现了真正的 GUI 框架内涵您可能听人们说过尽管某种技术不错但是它的外观仍然需要改进是的用 HTML 组件构建平淡无奇的页面的日子已经过去了如果使用 JSF 的话具有更高级 GUI 外观的日子就在眼前您会问怎么做呢?树形组件菜单组件和图形是已经存在的 UI 组件这些 JSF 一定要提供更进一步JSF 通过提供容易使用的 API 鼓励创建自定义组件
注: 这里所提到的 UI 组件是 Sun 提供的示例的一部分像所有规范一样实际的实现由不同的提供商完成
在传统的使用模型视图控制器(MVC)的 Web 应用程序中GUI 组件是由处理展示和业务逻辑的自定义标记所表示的这样就出现了必须编写与客户机设备打交道的代码的问题这会产生重复的代码使用 JSF 就不会有这个问题
JSF 结构将展示逻辑 (什么)与 UI 组件的业务逻辑 (为什么和如何)分离通过在 JSP 页面中使用 JSF 标记就可以将 renderer 与 UI 组件关联在一起一个 UI 组件可以用不同的 renderer 从而以不同的方式呈现特定于 UI 组件的代码在服务器上运行并且响应用户操作所产生的事件
JSFRI 提供了一个 render kit它带有一个自定义标记库用以从 UI 组件呈现 HTML它还提供了根据需要定制这些组件外观的能力如果需要特殊的组件那么可以为特定的客户机设备构造定制的标记并让它与一个子 UI 组件和定制的 renderer 相关联对于不同的设备您所需要做的就是指定不同的 renderer
JSF 和 UI 组件
您可能已经用 Java AWT 或者 Swing API 创建过 Java GUI 应用程序所以您应该熟悉 JSF 的 UIComponent (它与 AWT 或者 Swing 组件很相像)它储存其子组件的树(如果有的话)并为客户端发生的动作生成标准事件例如单击一个按钮以提交表单这些事件缓存在 FacesContext 中您可以用自定义标记关联每一个这种事件的处理程序例如用一个自定义的 ActionListener 处理用户单击或者表单提交
JSF UIComponentRenderer 和标记总是共同工作的所有 JSP 自定义标记都是通过继承 UIComponentTag 创建的doStart 和 doEnd 方法总是在 UIComponentTag 类中实现您只需在这些标记类中提供其他的功能
自定义标记UI 组件和 renderer 之间的关系客户机浏览器访问用 JSF 标记(jsf:myTag)表示 UI 组件(MyComponent)的 JSP 页面这个 UI 组件运行在服务器上并用适当的 renderer (MyRenderer)以 HTML 的形式呈现给客户这个 JSP 页面表现了在 JSFRI 中使用带自定义标记的用户界面组件而不是在 HTML 中对它们进行编码
展示了 h:panel:group 标记的使用这个标记用于将一个父组件下面的各个组件组织到一起如果与像 panel_grid 和 panel_data 这样的其他面板标记共同使用那么它会在运行时生成 HTML 表中的列的标记JSFRI提供的 html_basic 标记库用于表示像文本字段按钮这样的 HTML 组件
JSF 生命周期
JSF 生命周期包括六个阶段一个传入的请求可能会经历全部阶段也可能不经历任何阶段这取决于请求的类型在生命周期中发生的验证和转换错误以及响应的类型JSF 框架处理由 JSP 页生成的 Faces 请求并返回 faces 或者 nonfaces 响应
在提交一个 JSF 表单或者当用户单击指向在 URL 中具有 /faces 前缀的 URL 的链接时就会出现 faces 响应所有 faces 请求都由一个 FacesServlet 处理 这是 JSF 中的控制器
发送给一个 servlet 或者一个没有 JSF 组件的 JSP 页面的请求称为 nonfaces 请求如果结果页中有 JSF 标记那么它就称为 faces 响应如果没有 JSF 标记就是 nonfaces 响应
JSF 生命周期有六个阶段
重建请求树
应用请求值
进行验证
更新模型值
调用应用程序
呈现响应
根据 JSF 规范每一阶段表示请求处理生命周期的一个逻辑概念不过在 JSFRI 中这些阶段是由具有对应名字的实际类表示的下面一节描述了每一阶段是如何对请求进行处理并生成响应的您将首先看到的是处理一个 faces 请求所涉及的阶段然后是处理 faces 响应所涉及的阶段
处理 faces 请求
为了理解 JSF 请求处理请看 FlightSearchjsp这是清单 中的一个简单的 JSF 表单一个 JSF 页面基本上就是这个样子的这个 JSF 表单有输入文本字段 from 和 to citiesdeparture 和 return dates还有提交和重设表单的按钮(我们会在稍后分析清单中每一个标记的意义)现在假设提交这个表单产生了一个 faces 请求
这个请求被 FacesServlet 所接收并在向客户发回响应之前通过不同的阶段图 展示了如何对 JSF 请求进行处理让我们看一看这是如何进行的
接收请求
FacesServlet 接收请求并从 FacesContextFactory 得到 FacesContext 的一个实例
委托生命周期处理
FacesServlet 通过对在 faces 上下文中传递的 Lifecycle 实现调用 execute 方法将生命周期处理委托给 Lifecycle 接口
Lifecycle 执行每一阶段
Lifecycle 实现执行从重建组件树阶段开始的每一阶段
创建的组件树
在重建组件树阶段用 travelForm 中的组件创建一个组件树这个树以 UIForm 作为根用不同的文本字段和按钮作为其子组件
fromCity 字段有一个验证规则它规定其不能为空如 validate_required 标记所示这个标记将 fromCity 文本字段与一个 JSF Validator 链接起来
JSF 有几个内建的验证器相应的 Validator 是在这个阶段初始化的这个组件树缓存在 FacesContext 中并且这个上下文会在后面用于访问树及调用任何一个事件处理程序同时 UIForm 状态会自动保存所以当刷新这一页时就会显示表单的原始内容
从树中提取值
在应用请求值阶段JSF 实现遍历组件树并用 decode 方法从请求中提取值并在本地设置每一个组件如果在这个过程中出现了任何错误那么它们就在 FacesContext 中排队并在呈现响应阶段显示给用户
同时在这个阶段排队的所有由像单击按钮这样的用户操作产生的事件都广播给注册的侦听器单击 reset 按钮会将文本字段中的值重新设置为它们原来的值
处理验证
在处理验证阶段对在应用请求值阶段设置的本地值进行所有与各组件相关的验证当 JSF 实现对每一个注册的验证器调用 validate 方法时就会进入此阶段
如果任何一项验证失败那么生命周期就会进入呈现响应阶段在那里呈现带有错误信息的同一页面在这里所有在这一阶段排队的事件同样都会广播给注册的侦听器
JSF 实现处理源字段上的验证器如果数据是无效的那么控制就交给呈现响应阶段在这个阶段重新显示 FlightSearchjsp 并带有相关组件的验证错误通过在 JSP 页面中声明 output_errors页面中的所有错误都会显示在页面的底部
设置模型对象值
在更新模型值阶段成功处理了所有验证后JSF 实现就通过对每一组件调用 updateModel 方法用有效值设置模型对象值如果在将本地数据转换为由模型对象属性所指定的类型时出现任何错误那么生命周期就进入呈现响应阶段并将错误显示出来来自表单字段属性的值会填充为模型对象的属性值
可以调用 ActionListener
可以将一个 ActionListener 与一个用户操作如单击提交按钮相关联如清单 所示在调用应用程序阶段对 FlightSea