﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-☆蓝色梦想☆-随笔分类-JSF</title><link>http://www.blogjava.net/zlsunnan/category/5745.html</link><description>世界总是反反覆覆错错落落地飘去 来不及叹息 生活不是平平淡淡从从容容的东西 不能放弃</description><language>zh-cn</language><lastBuildDate>Fri, 02 Mar 2007 03:33:14 GMT</lastBuildDate><pubDate>Fri, 02 Mar 2007 03:33:14 GMT</pubDate><ttl>60</ttl><item><title>JavaServer Faces in Action</title><link>http://www.blogjava.net/zlsunnan/archive/2005/12/10/23243.html</link><dc:creator>☆蓝色梦想☆</dc:creator><author>☆蓝色梦想☆</author><pubDate>Sat, 10 Dec 2005 03:09:00 GMT</pubDate><guid>http://www.blogjava.net/zlsunnan/archive/2005/12/10/23243.html</guid><wfw:comment>http://www.blogjava.net/zlsunnan/comments/23243.html</wfw:comment><comments>http://www.blogjava.net/zlsunnan/archive/2005/12/10/23243.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zlsunnan/comments/commentRss/23243.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zlsunnan/services/trackbacks/23243.html</trackback:ping><description><![CDATA[<P><A id=spaceurl style="COLOR: white" href="http://realzar.5u6.net/" target=_blank><STRONG><FONT color=#000000>http://realzar.5u6.net</FONT></STRONG></A> </P><img src ="http://www.blogjava.net/zlsunnan/aggbug/23243.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zlsunnan/" target="_blank">☆蓝色梦想☆</a> 2005-12-10 11:09 <a href="http://www.blogjava.net/zlsunnan/archive/2005/12/10/23243.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>选择JSF不选Struts</title><link>http://www.blogjava.net/zlsunnan/archive/2005/12/08/22940.html</link><dc:creator>☆蓝色梦想☆</dc:creator><author>☆蓝色梦想☆</author><pubDate>Thu, 08 Dec 2005 01:44:00 GMT</pubDate><guid>http://www.blogjava.net/zlsunnan/archive/2005/12/08/22940.html</guid><wfw:comment>http://www.blogjava.net/zlsunnan/comments/22940.html</wfw:comment><comments>http://www.blogjava.net/zlsunnan/archive/2005/12/08/22940.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zlsunnan/comments/commentRss/22940.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zlsunnan/services/trackbacks/22940.html</trackback:ping><description><![CDATA[一个客户不知道该选用Struts还是JSF。就像你预料的那样，我通常会问：这2中框架之间有什么区别？当然，除了我的这个客户外很多人都面临这样的选择。 
<P class=main>&nbsp;&nbsp;&nbsp; 总的来说，我建议在新项目中优先考虑JSF。虽然常常有一些商业上的因素迫使我们为现有的项目选择了Struts，而且那些解决方案还有待考验，但是，让我们面对一个事实：JSF比Struts好多了。 </P>
<P class=main>&nbsp;&nbsp;&nbsp; 下面是我选择JSF而不选Struts的十大理由： </P>
<UL>
<LI>1.Components(组件) 
<LI>2.Render Kits 
<LI>3.Renderers 
<LI>4.Value Binding Expressions（值绑定表达式） 
<LI>5.Event Model（事件模型） 
<LI>6.Extensibility(可扩展性) 
<LI>7.Managed Beans(Dependency Injection 依赖注入) 
<LI>8.POJO Action Methods 
<LI>9.JSF is the standard Java-based web app framework (JSF是java web应用程序的标准框架) 
<LI>10.There's only one Struts(只有一个Struts) </LI></UL>
<P class=main>&nbsp;&nbsp;&nbsp; 10.There's only one Struts(只有一个Struts) Struts 是一个开源产品，然而JSF是一个标准。这个细节常常被新的JSF学习者忽略，其实这是显而易见的，因为我们有多个JSF的实现。虽然JSF还很不成熟，但是我们已经有了2个优秀的JSF实现可以选择：Sun的参考实现和Apache的MyFaces。另一方面，我们只有一个Struts。 </P>
<P class=main>&nbsp;&nbsp;&nbsp; 9.JSF is the standard(JSF是标准) JEE 5.0要提供一个JSF的实现，这表明JSF不久将会无处不在。这可能与你无关，但是和工具供应商密切相关。现在大概有50个java web应用程序框架，工具供应商不会情愿去支持一个特别的框架，但是他们会毫不犹豫的去支持一个标准。而且不止供应商，开源项目也会迅速的聚集在JSF的四周，争先恐后的去实现相同的功能。比如说，直到我们去实现本质上和Shale的Tapestry差不多的视图的时候，我才知道Facalets。（从长远来看，我相信这种冗余是件好事，会给我们带来好处） </P>
<P class=main>&nbsp;&nbsp;&nbsp; 8.POJO Action Methods Struts的行为是和 Struts的API绑定在一起的，但是JSF的行为方法可以在POJPO中实现。这意味着你不用在表单和模型对象之间实现一个多余的行为层。顺便说一下，在JSF里面没有行为对象，行为在模型对象中实现。但是也请注意一点：如果你愿意你也可以生成与JSF独立的行为对象。在Struts里面，你有 Form Bean和Action Bean。Form Bean包含数据而Action Bean包含逻辑。OO狂会想去合并前2者，在Struts你办不到。但是在JSF中，你可以分开数据和逻辑，也可以合并到一个对象中，一切由你决定。 </P>
<P class=main>&nbsp;&nbsp;&nbsp; 7.Managed Beans(Dependency Injection 依赖注入) 和Spring一样，JSF也使用了依赖注入（DJ）（或控制反转（IoC））去实例化和初始化Bean。Struts的确为你生成了Form Bean和Action Bean，但是JSF可以为你生成各种各样的Managed Bean。 </P>
<P class=main>&nbsp;&nbsp;&nbsp; 6.Extensibility(可扩展性) 这个很重要。JSF有6个对象实现了这个框架的大部分功能，而且你可以很容易的用你自己的实现代替原有实现。比如你想加一个自定义参数在JSF表达式语言里面，或是添加一个自己的视图控制器以便于区分组件和HTML。事实上Shale实现了上面的功能。如果你还没有满足，JSF提供了几个地方你可以轻松的控制JSF的生命周期。Shale给你的会更多。</P>
<P class=main>&nbsp;&nbsp;&nbsp; 5.Event Model（事件模型） JSF的事件模型使你可以对值改变，动作，JSF生命周期阶段变换等作出反应。在JSF1.1中，那些事件都是在服务器端处理的，这肯定是一个缺陷，好在JSF2.0计划支持客户端事件，拭目以待吧。 </P>
<P class=main>&nbsp;&nbsp;&nbsp; 4.Value Binding Expressions（值绑定表达式） 在Struts 中，你负责把数据从Form传递到模型对象。你实现的Action的execute方法是把Form作为一个参数。然后你再手动的把数据从Form Bean里面取出放到模型对象里面。你要为应用里面的每个Form做这些事情，然而在JSF里面，你只需像这样：#{model.property} 就够了，其他的交给JSF来处理。 </P>
<P class=main>&nbsp;&nbsp;&nbsp; 3.Renderers 你有看过Struts的标签的源代码吗？它直接生成HTML。JSF组件标签什么都不生成，它和服务器上的一对component-renderer对应。Component维护组件状态，rendered负责获得视图。重点是renderers是可插拔的，即你可以根据自己需求实现然后替代掉默认实现。比如说我在NFJS上面的Felix谈话中举例说明了怎么去实现一个自定义的label renderer。你只需要配置你的renderer，JSF就会自动在你的应用程序里面使用他。 </P>
<P class=main>&nbsp;&nbsp;&nbsp; 2.Render Kits 在几年前我曾经有份Struts咨询工作，我们必须同时支持浏览器和无线设备，非常痛苦。但是用JSF来完成那个任务非常容易，因为你可以生成你自己的render kit－为一种特定显示技术的renderers的集合－然后配置到JSF里面。 </P>
<P class=main>&nbsp;&nbsp;&nbsp; 1.Components(组件) 组件是Struts和JSF之间最大的区别。就像Swing一样，JSF提供丰富的底层构件去开发组件然后添加到标准的组件集。那些底层构件让你很容易的生成自己的组件并且和别人共享。现在我们到处都能看到自定义组件跳出来，比如说Oracle的ADF和MyFaces，两者都提供了丰富的组件集，就像 javascript日历，tree等等。当然，组件只是一部分。典型的是，组件都和一个独立的renderer对应，这给我们带来了真正的好处（看第3 条）。但是和JSF中的很多东西一样，你不一定要墨守成规。只要你愿意，你可以实现render自己的组件，虽然这样你会失去给组件加入别的 renderer的能力。 </P><img src ="http://www.blogjava.net/zlsunnan/aggbug/22940.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zlsunnan/" target="_blank">☆蓝色梦想☆</a> 2005-12-08 09:44 <a href="http://www.blogjava.net/zlsunnan/archive/2005/12/08/22940.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>集成 Struts、Tiles 和 JavaServer Faces</title><link>http://www.blogjava.net/zlsunnan/archive/2005/12/08/22939.html</link><dc:creator>☆蓝色梦想☆</dc:creator><author>☆蓝色梦想☆</author><pubDate>Thu, 08 Dec 2005 01:26:00 GMT</pubDate><guid>http://www.blogjava.net/zlsunnan/archive/2005/12/08/22939.html</guid><wfw:comment>http://www.blogjava.net/zlsunnan/comments/22939.html</wfw:comment><comments>http://www.blogjava.net/zlsunnan/archive/2005/12/08/22939.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zlsunnan/comments/commentRss/22939.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zlsunnan/services/trackbacks/22939.html</trackback:ping><description><![CDATA[<BLOCKQUOTE>您是否想将 JavaServer Faces (JSF)的强大前端功能、Tiles 的内容格式编排优势和 Struts controller 层的灵活性都加入到您的J2EE Web 应用程序中？企业级 Java 专家 Srikanth Shenoy 和 Nithin Mallya 为您展示了如何将这三者的功能集成到一起。本文演示了如何在 Struts-Faces集成库中定制类以使得它们可以与 Tiles 和 JSF 一同使用，并用一个实际的例子解释了这个过程背后的基本原理以及如何使用新的一组类的细节。</BLOCKQUOTE>
<P>将 Struts、Tiles 和 JavaServer Faces (JSF) 一起使用，开发人员可以实现易于管理和重用的、健壮的、界面清晰的 Web 应用程序。</P>
<P>Struts 框架推出已经有一段时间了，它已经成为在开发 J2EE Web 应用程序时开发人员所采用的事实上的标准。Tiles 框架是在 Struts 之后不久出现的，它通过为开发人员提供用组件组装展示页面的能力开拓了自己的生存环境。JSF 是 Web 应用程序框架中最新的成员，它提供了验证用户输入和处理用户事件的机制，最重要的是，这是一种以协议无关的方式呈现用户界面组件的方法(有关这些 技术的概况，参见本文相关页面“ <A href="http://www.ibm.com/developerworks/java/library/j-integrate/side-integrate.html"><FONT color=#5c81a7>The major players</FONT></A>”)。 </P>
<P>尽管 Struts 和 JSF 中有一些功能是重叠的，但是它们在其他方面起到了互为补充的作用。这三种技术的结合可以为开发 Web 应用程序、组织其展示和以协议无关的方式呈现定制的用户界面(UI)组件提供一种高效的途径。</P>
<P>为了运行本文中的示例代码，需要 Struts 1.1、Tiles、JavaServer Faces Reference Implementation (JSF-RI) Early Access Release 4.0 以及 Struts-Faces 0.4。Jakarta 项目提供的 Struts 1.1 发行版本将 Struts 和 Tiles 捆绑发布。还可以从 Jakarta 项目上下载 Struts-Faces 集成库。JSF-RI 是 Sun 的 Web 开发工具包(Web Services Developer Pack)的一部分(在 <A href="http://www-128.ibm.com/developerworks/cn/java/j-integrate/#resources"><FONT color=#996699>参考资料</FONT></A>中有这些下载和示例代码的链接)。 </P>
<P>现在回到集成三种技术的细节上。首先有个坏消息：在本文发表的时候，这三种技术是不能直接互操作的。好消息是：在本文中，我们展示了集成 Struts、Tiles 和 JSF 的方法。我们假设您已经了解 Struts 和 Tiles。对 JSF 有一些了解会有帮助(参阅 <A href="http://www-128.ibm.com/developerworks/cn/java/j-integrate/#resources"><FONT color=#996699>参考资料</FONT></A>中提供的 developerWorks 上的 JSF 教程的链接)，但是不了解也不妨碍对本文的理解。 </P>
<P><A name=1><SPAN class=atitle><FONT face=Arial size=4>JSF 简介</FONT></SPAN></A></P>
<P>JSF 应用程序是使用 JSF 框架的普通 J2EE Web 应用程序，JSF 框架提供了丰富的 GUI 组件模型，这些模型体现了真正的 GUI 框架内涵。您可能听人们说过，尽管某种技术不错，但是它的外观仍然需要改进。是的，用 HTML 组件构建平淡无奇的页面的日子已经过去了，如果使用 JSF 的话，具有更高级 GUI 外观的日子就在眼前。您会问，怎么做呢？树形组件、菜单组件和图形是已经存在的 UI 组件，这些 JSF 一定要提供。更进一步，JSF 通过提供容易使用的 API 鼓励创建自定义组件。</P>
<P><B>注:</B> 这里所提到的 UI 组件是 Sun 提供的示例的一部分。像所有规范一样，实际的实现由不同的提供商完成。 </P>
<P>在传统的使用模型-视图-控制器(MVC)的 Web 应用程序中，GUI 组件是由处理展示和业务逻辑的自定义标记所表示的。这样就出现了必须“编写与客户机设备打交道的代码”的问题，这会产生重复的代码。使用 JSF 就不会有这个问题。</P>
<P>JSF 结构将 <I>展示逻辑</I> (“什么”)与 UI 组件的 <I>业务逻辑</I> (“为什么”和“如何”)分离。通过在 JSP 页面中使用 JSF 标记，就可以将 renderer 与 UI 组件关联在一起。一个 UI 组件可以用不同的 renderer 从而以不同的方式呈现。特定于 UI 组件的代码在服务器上运行，并且响应用户操作所产生的事件。 </P>
<P>JSF-RI 提供了一个 render kit，它带有一个自定义标记库，用以从 UI 组件呈现 HTML。它还提供了根据需要定制这些组件外观的能力。如果需要特殊的组件，那么可以为特定的客户机设备构造定制的标记并让它与一个子 UI 组件和定制的 renderer 相关联。对于不同的设备，您所需要做的就是指定不同的 renderer。</P>
<P><A name=N1009F><SPAN class=smalltitle><STRONG><FONT face=Arial>JSF 和 UI 组件</FONT></STRONG></SPAN></A></P>
<P>您可能已经用 Java AWT 或者 Swing API 创建过 Java GUI 应用程序，所以您应该熟悉 JSF 的 <CODE>UIComponent</CODE> (它与 AWT 或者 Swing 组件很相像)。它储存其子组件的树(如果有的话)并为客户端发生的动作生成标准事件，例如单击一个按钮以提交表单。这些事件缓存在 <CODE>FacesContext</CODE> 中。您可以用自定义标记关联每一个这种事件的处理程序。例如，用一个自定义的 <CODE>ActionListener</CODE> 处理用户单击或者表单提交。 </P>
<P>JSF <CODE>UIComponent</CODE> 、 <CODE>Renderer</CODE> 和标记总是共同工作的。所有 JSP 自定义标记都是通过继承 <CODE>UIComponentTag</CODE> 创建的。 <CODE>doStart</CODE> 和 <CODE>doEnd</CODE> 方法总是在 <CODE>UIComponentTag</CODE> 类中实现。您只需在这些标记类中提供其他的功能。 </P>
<P>图 1展示了自定义标记、UI 组件和 renderer 之间的关系。客户机浏览器访问用 JSF 标记( <CODE>jsf:myTag</CODE> )表示 UI 组件( <CODE>MyComponent</CODE> )的 JSP 页面。这个 UI 组件运行在服务器上，并用适当的 renderer ( <CODE>MyRenderer</CODE> )以 HTML 的形式呈现给客户。这个 JSP 页面表现了在 JSF-RI 中使用带自定义标记的用户界面组件而不是在 HTML 中对它们进行编码。 </P>
<P>例如，图 1 展示了 <CODE>h:panel:group</CODE> 标记的使用。这个标记用于将一个父组件下面的各个组件组织到一起。如果与像 <CODE>panel_grid</CODE> 和 <CODE>panel_data</CODE> 这样的其他面板标记共同使用，那么它会在运行时生成 HTML 表中的列的标记。JSF-RI-提供的 <I>html_basic</I> 标记库用于表示像文本字段、按钮这样的 HTML 组件。 </P><BR><A name=IDA0FCFG><B>图1. 呈现一个 JSF 页面 </B></A><BR><IMG height=228 alt="Rendering a JSF page" src="http://www-128.ibm.com/developerworks/cn/java/j-integrate/images/figure1.gif" width=456> <BR>
<P><A name=N100FF><SPAN class=smalltitle><STRONG><FONT face=Arial>JSF 生命周期</FONT></STRONG></SPAN></A></P>
<P>JSF 生命周期包括六个阶段：一个传入的请求可能会经历全部阶段，也可能不经历任何阶段，这取决于请求的类型、在生命周期中发生的验证和转换错误以及响应的类型。JSF 框架处理由 JSP 页生成的 <I>Faces 请求</I>，并返回 <I>faces</I>或者 <I>non-faces 响应</I>。 </P>
<P>在提交一个 JSF 表单，或者当用户单击指向在 URL 中具有 <I>/faces </I>前缀的 URL 的链接时，就会出现 <I>faces 响应</I>。所有 faces 请求都由一个 <CODE>FacesServlet</CODE> 处理 -- 这是 JSF 中的控制器。 </P>
<P>发送给一个 servlet 或者一个没有 JSF 组件的 JSP 页面的请求称为 <I>non-faces 请求</I>。如果结果页中有 JSF 标记，那么它就称为 <I>faces 响应</I>，如果没有 JSF 标记，就是 <I>non-faces 响应</I>。 </P>
<P>JSF 生命周期有六个阶段：</P>
<UL>
<LI>重建请求树 
<LI>应用请求值 
<LI>进行验证 
<LI>更新模型值 
<LI>调用应用程序 
<LI>呈现响应 </LI></UL>
<P>根据 JSF 规范，每一阶段表示请求处理生命周期的一个逻辑概念。不过在 JSF-RI 中，这些阶段是由具有对应名字的实际类表示的。下面一节描述了每一阶段是如何对请求进行处理并生成响应的。您将首先看到的是处理一个 faces 请求所涉及的阶段，然后是处理 faces 响应所涉及的阶段。</P>
<P><A name=N10145><SPAN class=smalltitle><STRONG><FONT face=Arial>处理 faces 请求</FONT></STRONG></SPAN></A></P>
<P>为了理解 JSF 请求处理，请看 FlightSearch.jsp，这是 <A href="http://www-128.ibm.com/developerworks/cn/java/j-integrate/#listing1"><FONT color=#996699>清单 1</FONT></A>中的一个简单的 JSF 表单。一个 JSF 页面基本上就是这个样子的。这个 JSF 表单有输入文本字段 <I>from</I>和 <I>to cities</I>、 <I>departure</I> 和 <I>return dates</I>，还有提交和重设表单的按钮(我们会在稍后分析清单1中每一个标记的意义)。现在，假设提交这个表单产生了一个 faces 请求。 </P>
<P>这个请求被 <CODE>FacesServlet</CODE> 所接收、并在向客户发回响应之前通过不同的阶段。 <A href="http://www-128.ibm.com/developerworks/cn/java/j-integrate/#fig2"><FONT color=#996699>图 2</FONT></A>展示了如何对 JSF 请求进行处理。让我们看一看这是如何进行的。 </P>
<P><B>1. 接收请求</B> <BR><CODE>FacesServlet</CODE> 接收请求并从 <CODE>FacesContextFactory</CODE> 得到 <CODE>FacesContext</CODE> 的一个实例。 </P>
<P><B>2. 委托生命周期处理</B> <BR><CODE>FacesServlet</CODE> 通过对在 faces 上下文中传递的 <CODE>Lifecycle</CODE> 实现调用 <CODE>execute</CODE> 方法将生命周期处理委托给 <CODE>Lifecycle </CODE>接口。 </P>
<P><B>3. Lifecycle 执行每一阶段</B> <BR><CODE>Lifecycle</CODE> 实现执行从重建组件树阶段开始的每一阶段。 </P>
<P><B>4. 创建的组件树</B> <BR>在重建组件树阶段，用 <CODE>travelForm</CODE> 中的组件创建一个组件树。这个树以 <CODE>UIForm</CODE> 作为根，用不同的文本字段和按钮作为其子组件。 </P>
<P><CODE>fromCity</CODE> 字段有一个验证规则，它规定其不能为空，如 <CODE>validate_required</CODE> 标记所示。这个标记将 <CODE>fromCity</CODE> 文本字段与一个 JSF <CODE>Validator</CODE> 链接起来。 </P>
<P>JSF 有几个内建的验证器。相应的 <CODE>Validator</CODE> 是在这个阶段初始化的。这个组件树缓存在 <CODE>FacesContext</CODE> 中、并且这个上下文会在后面用于访问树及调用任何一个事件处理程序。同时 <CODE>UIForm</CODE> 状态会自动保存。所以，当刷新这一页时，就会显示表单的原始内容。 </P>
<P><B>5. 从树中提取值</B> <BR>在应用请求值阶段，JSF 实现遍历组件树并用 <CODE>decode</CODE> 方法从请求中提取值，并在本地设置每一个组件。如果在这个过程中出现了任何错误，那么它们就在 <CODE>FacesContext</CODE> 中排队并在呈现响应阶段显示给用户。 </P>
<P>同时，在这个阶段排队的所有由像单击按钮这样的用户操作产生的事件，都广播给注册的侦听器。单击 reset 按钮会将文本字段中的值重新设置为它们原来的值。</P>
<P><B>6. 处理验证</B> <BR>在处理验证阶段，对在应用请求值阶段设置的本地值进行所有与各组件相关的验证。当 JSF 实现对每一个注册的验证器调用 <CODE>validate</CODE> 方法时就会进入此阶段。 </P>
<P>如果任何一项验证失败，那么生命周期就会进入呈现响应阶段，在那里呈现带有错误信息的同一页面。在这里，所有在这一阶段排队的事件同样都会广播给注册的侦听器。</P>
<P>JSF 实现处理源字段上的验证器。如果数据是无效的，那么控制就交给呈现响应阶段，在这个阶段重新显示 FlightSearch.jsp 并带有相关组件的验证错误。通过在 JSP 页面中声明 <CODE>output_errors，</CODE> ，页面中的所有错误都会显示在页面的底部。 </P>
<P><B>7. 设置模型对象值</B> <BR>在更新模型值阶段，成功处理了所有验证后，JSF 实现就通过对每一组件调用 <CODE>updateModel</CODE> 方法用有效值设置模型对象值。如果在将本地数据转换为由模型对象属性所指定的类型时出现任何错误，那么生命周期就进入呈现响应阶段，并将错误显示出来。来自表单字段属性的值会填充为模型对象的属性值。 </P>
<P><B>8. 可以调用 ActionListener </B><BR>可以将一个 <CODE>ActionListener</CODE> 与一个用户操作，如单击提交按钮相关联，如 <A href="http://www-128.ibm.com/developerworks/cn/java/j-integrate/#listing1"><FONT color=#996699>清单 1</FONT></A>所示。在调用应用程序阶段，对 <CODE>FlightSearchActionListener</CODE> 调用了 <CODE>processAction</CODE> 方法。在实际应用中， <CODE>processAction</CODE> 方法在调用后会搜索数据以找出满足条件的航班，并从组件的 action 属性中提取输出。 </P>
<P>在本文提供的这个示例 Web 应用程序中，我们使用了静态数据表示航班表。这个方法还将提取的 action 属性发送给 <CODE>NavigationHandler</CODE> 实现。 <CODE>NavigationHandler</CODE> 查询 faces-config.xml 文件 -- 这是 JSF 的默认应用程序配置文件 -- 以根据这一输出确定下一页是什么。 </P>
<P><B>9. 呈现响应</B> 在呈现响应阶段，如果在 faces 上下文中没有错误，就显示由查询配置文件得到的这一页 FlightList.jsp。如果是因为前面任一阶段的错误而到达这一阶段的，那么就会重新显示带有错误信息的 FlightSearch.jsp。 </P>
<P><B>图 2. 处理一个 JSF 请求</B> <BR><A href="http://www.ibm.com/developerworks/java/library/j-integrate/side-fig2.html"><FONT color=#996699>单击这里</FONT></A>以观看该图。 </P><BR><A name=listing1><B>清单 1. FlightSearch.jsp，一个简单的 JSF 表单</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %&gt;
&lt;%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %&gt;

&lt;f:use_faces&gt;
 &lt;h:form id="flightForm" formName="flightForm" &gt;
   &lt;h:input_text id="fromCity" valueRef="FlightSearchBean.fromCity"&gt;
  	&lt;f:validate_required/&gt;
   &lt;h:input_text/&gt;

   &lt;h:input_text id="toCity" valueRef="FlightSearchBean.toCity"&gt;
   &lt;h:input_text id="departureDate" 
     valueRef="FlightSearchBean.departureDate"&gt;
   &lt;h:input_text id="arrivalDate" 
     valueRef="FlightSearchBean.arrivalDate"&gt;

   &lt;h:command_button id="submit" action="success" 
     label="Submit" commandName="submit" &gt;
       &lt;f:action_listener 
         type="foo.bar.FlightSearchActionListener"/&gt;
   &lt;/h:command_button&gt;
   &lt;h:command_button id="reset" action="reset" label="Reset" 
     commandName="reset" /&gt;

   &lt;h:output_errors/&gt;
 &lt;/h:form&gt;
&lt;/f:use_faces&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>在这段代码中使用了两个 JSF-RI 的标记库。 <I>html_basic</I> 标记库定义了 HTML 组件常用的标记，而 <I>jsf-core</I> 标记库包含用于注册侦听器和验证器的标记。其他标记有： </P>
<UL>
<LI><CODE>f:use_faces</CODE> 标记向 JSF 实现表明后面的标记是 faces 标记。 
<LI><CODE>f:validate_required</CODE> 标记表明它所在的字段(在 <CODE>FlightSearchBean</CODE> 中是 <CODE>fromCity</CODE> 字段)在提交表单时应该有值。 
<LI><CODE>h:form</CODE> 和 <CODE>h:input_text</CODE> 标记分别表示一个名为 <CODE>flightSearchForm</CODE> 的 HTML 表单和各种文本字段。 
<LI><CODE>h:command_button</CODE> 标记用于表示提交和重设按钮。 
<LI>最后， <CODE>h:output_errors</CODE> 标记类似于 Struts <CODE>html:errors</CODE> 标记，用于显示在表单字段验证中出现的任何错误。 </LI></UL>
<P>一个名为 FlightSearchBean 的 JavaBean 表示在更新模型值阶段用 <CODE>UIComponent</CODE> 数据更新的模型。通常在 JSP 页中 JavaBean 是用 <CODE>jsp:useBean</CODE> 标记声明的。您可能注意到了在 FlightSearch.jsp 中没有这样做。这是因为可以使用 JSF 的一个名为 <I>Managed Beans</I> 的功能，在 faces 配置文件中声明所有 JSP 页面使用的 JavaBeans 组件。在开始时，servlet 容器会初始化这些 JavaBeans 组件。faces-config.xml 文件中的 FlightSearchBean 入口如清单 2所示： </P><BR><A name=listing2><B>清单 2. faces-config.xml 的 TravelInfoBean入口</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;managed-bean&gt;
  &lt;managed-bean-name&gt;FlightSearchBean&lt;/managed-bean-name&gt;
  &lt;managed-bean-class&gt;
    foo.bar.FlightSearchBean
  &lt;/managed-bean-class&gt;
  &lt;managed-bean-scope&gt;session&lt;/managed-bean-scope&gt;
&lt;/managed-bean&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>现在让我们看一看这些阶段是如何处理响应的。</P>
<P><A name=N102A9><SPAN class=smalltitle><STRONG><FONT face=Arial>呈现 faces 响应</FONT></STRONG></SPAN></A></P>
<P>一个 faces 响应是由 Faces 应用程序在生成包含 JSF 标记的 JSP 页时生成的。这个响应可以是 JSF 应用程序的 faces 或者 non-faces 响应。</P>
<P>在我们的例子中，清单 1 中页面的呈现是一个 faces 响应。您可能熟悉 <CODE>Tag</CODE> 接口的 <CODE>doStartTag()</CODE> 和 <CODE>doEndTag()</CODE> 方法。在 JSF 和 Struts-Faces 中，每一个标记都是从 <CODE>UIComponentTag</CODE> 扩展的。 <CODE>UIComponentTag</CODE> 实现了 <CODE>doStartTag()</CODE> 和 <CODE>doEndTag()</CODE> 方法。 </P>
<P>它还有两个抽象方法 <CODE>getComponentType()</CODE> 和 <CODE>getRendererType()。</CODE> 通过在具体的标记类中实现这两个方法，就可以分别指定组件和 renderer 的类型。 </P>
<P>考虑一个带有文本字段的简单 JSF 表单。在呈现 JSF 表单时执行以下一系列步骤。</P>
<P><B>1. 调用 doStartTag() 方法</B> <BR>Servlet 窗口对 <CODE>FormTag</CODE> 调用 <CODE>doStartTag()</CODE> 方法。 </P>
<P><B>2. 得到 UIComponent</B> <BR><CODE>FormTag</CODE> 从 <CODE>getComponentType() </CODE>方法得到其 <CODE>UIComponent。</CODE> <CODE>UIComponentTag</CODE> ( <CODE>FormTag</CODE> <CODE>的父组件)使用 <CODE>getComponentType()</CODE> 以从 faces-config.xml 文件中查询这个组件的类名，并创建 <CODE>UIComponent(FormComponent</CODE> )的一个实例。 </P>
<P><B>3. 得到 renderer </B><BR>下一步， <CODE>FormTag</CODE> 从 <CODE>getRendererType</CODE> 方法中得到其 renderer 。与组件类型一样，renderer 名是在 faces-config.xml 文件中查询的。 </P>
<P><B>4. 调用编码方法</B> <BR>在创建了 <CODE>FormComponent</CODE> 和 <CODE>FormRenderer</CODE> 后，对 <CODE>FormComponent</CODE> 调用 <CODE>encodeBegin()</CODE> 方法。每一个标记的呈现都由 <CODE>encodeBegin()</CODE> 开始、由 <CODE>encodeEnd() </CODE>结束。 <CODE>encodeBegin()</CODE> 方法是按嵌套的顺序调用的。 </P>
<P><B>5. 结束标记和呈现 HTML</B> <BR>servlet 容器对标记调用 <CODE>doEndTag()</CODE> 方法。以嵌套的反顺序对每一个组件调用 <CODE>encodeEnd()</CODE> 方法。在最后，表单和所有嵌套的组件都呈现为 HTML。这时，HTML 就生成完毕，并呈现出对应于 JSP 的 HTML。 </P>
<P>图 3 显示构成生成 faces 响应的事件序列。</P>
<P><A name=fig3><B>图 3. 呈现一个 faces 响应</B> <BR><A href="http://www.ibm.com/developerworks/java/library/j-integrate/side-fig3.html"><FONT color=#5c81a7>单击这里</FONT></A>以查看该图。 </P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-integrate/#main"><B><FONT face=Verdana color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=2><SPAN class=atitle><FONT face=Arial size=4>为什么将这三者集成为一体？</FONT></SPAN></A></P>
<P>随着 JSP 和相关规范的不断发展，像 JSF 和 JSP 标记库(或者 JSTL，它使用简单的标记封装许多 JSP 应用程序常用的核心功能)这样的新标准正在不断出现。下面是使用集成为一个整体的新技术一些好处：</P>
<UL>
<LI><B>更清晰地分离行为和展示。</B> 将标记、 renderer 和组件分离，就可以更好地定义开发周期中的页面作者和应用程序开发人员的作用。 <BR>
<LI><B>改变一个组件的展示不会有雪崩效应。</B>现在您可以容易地只对 renderer 作出改变。在传统的 MVC 模型中，由于没有这种分离，对于标记的任何改变都需要改变业务逻辑。现在再不需要这样了。 <BR>
<LI><B>renderer 无关性。</B> 也可以说是协议无关性，通过对带有多个 renderer 的多种展示设备重复使用组件逻辑实现。使用不同 renderer 的能力使得不再需要对特定的设备编写整个表示层代码。 <BR>
<LI><B>组装和重用自定义组件的标准。</B>JSF 的考虑范围超出了“表单和字段”，它提供了丰富的组件模型用以呈现自定义 GUI 组件。用 JSF 可以定制每一个组件在页面中的外观和行为。开发人员还拥有创建他们自己的 GUI 组件(如菜单和树)的能力，这些组件可以用简单的自定义标记容易地加入到任何 JSP 页面中。就像 AWT 和 Swing 所提供的 Java 前端 GUI 组件一样，我们可以在我们的 Web 页而中有自定义的组件，它们使用自己的事件处理程序并有定制的外观。这是 Web 层的 GUI 天堂！ </LI></UL>
<P>Struts 是一种已经拥有大量客户基础的框架。许多 IT 部门认识到这种 MVC 框架的价值并使用它有一段时间了。JSF 没有像 Structs 这样强大的控制器结构，也没有像它那样标准化的 <CODE>ActionForm</CODE> 和 <CODE>Actions</CODE> （及它们声明的能力）。将 Tiles 集成到集合体中，就给了自己重复使用和以无缝的方式改变公司布局的能力。 </P>
<P>移植支持 JSF 的 Struts 应用程序的挑战是双重的。首先，Struts 标记不是 JSF 兼容的。换句话说，它们没有像 JSF 规范所规定的那样扩展 <CODE>UIComponentTag</CODE> ，所以，JSF 不能解释它们并关联到 <CODE>UIComponent</CODE> 和 <CODE>Renderers</CODE> 。 </P>
<P>其次，在 <CODE>FacesServlet</CODE> 与 Struts <CODE>RequestProcessor</CODE> 之间没有链接。在 Struts 应用程序中， <CODE>RequestProcessor</CODE> 负责用 <CODE>ActionForm</CODE> 和 <CODE>Actions</CODE> 类中的回调方法显示。 <CODE>ActionForm</CODE> 属性和 <CODE>validate()</CODE> 的 getter 和 setter 是 <CODE>ActionForm</CODE> 中的回调方法。对于 <CODE>Action</CODE> ， <CODE>execute()</CODE> 是回调方法。除非调用了 <CODE>RequestProcessor</CODE> ，否则 Struts <CODE>ActionForm</CODE> 和 <CODE>Actions</CODE> 类中的回调方法没有机会调用业务逻辑。 </P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-integrate/#main"><B><FONT face=Verdana color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=3><SPAN class=atitle><FONT face=Arial size=4>将 Struts 和 JSF 与 Struts-Faces 集成</FONT></SPAN></A></P>
<P>这里，您可能会问是否有软件可以帮助将 Struts 与 JSF 集成，或者是否必须自己编写集成软件。</P>
<P>好消息是已经有这样的软件了。 <I>Struts-Faces</I> 是一个早期发布的 Struts JSF 集成库。这个库是由 Craig McClanahan 创建的，它使得将现有 Struts 应用程序移植到 JSF 变得容易了(保留了对现有 Struts 投资的价值)。Struts-Faces 还力图与 JSF 进行简洁的集成，这样就可以在前端使用 JSF，同时后端仍然有熟悉的 Struts 组件。 </P>
<P>图 4 展示了 Struts-Faces 与 JSF 类之间的关系。蓝色的类属于 Struts-Faces。</P>
<P><B>图 4. Struts-Faces 类图</B> <BR><A href="http://www.ibm.com/developerworks/java/library/j-integrate/side-fig4.html"><FONT color=#5c81a7>单击这里</FONT></A>以查看该图。 </P>
<P>下面是 Struts-Faces 的主要组件：</P>
<UL>
<LI><CODE>FacesRequestProcessor</CODE> 类，它处理所有 faces 请求。这个类继承了常规 Struts <CODE>RequestProcessor</CODE> ，并处理 faces 请求。Non-faces 请求发送给出其父类 -- <CODE>RequestProcessor</CODE> 。 <BR>
<LI><CODE>ActionListenerImpl</CODE> 类，它处理像提交表单或者单击链接这样的 <CODE>ActionEvent</CODE> 。这个类用于代替由 JSF-RI 提供的默认 <CODE>ActionListener</CODE> 实现。只要在一个 faces 请求中生成 <CODE>ActionEvent</CODE> ，就会对 <CODE>ActionListenerImpl</CODE> 调用 <CODE>processAction()</CODE> 方法、并将 <CODE>ActionEvents</CODE> 转送给 <CODE>FacesRequestProcessor</CODE> 。这很有意思，因为 <CODE>RequestProcessor</CODE> 通常只由 <CODE>ActionServlet</CODE> 调用以处理 HTTP 请求。 <BR>
<LI><CODE>FormComponent</CODE> 类，它扩展了 JSF Form 组件，但是是在 Struts 生命周期内调用的。 <BR>
<LI><CODE>FormComponent</CODE> 的 renderer 和标记。 <BR>
<LI>只用于输出的数据标记和 renderer ，这里不需要分离组件。例如， <CODE>ErrorsTag</CODE> 和 <CODE>ErrorsRenderer</CODE> 用于在 HTML 中显示表单错误。 <BR>
<LI><CODE>ServletContextListener</CODE> 的名为 <CODE>LifeCycleListener</CODE> 的实现。它用于在初始化时注册相应的 <CODE>RequestProcessor</CODE> 。 <BR>
<LI>faces-config.xml 文件。这个文件已经捆绑在 struts-faces.jar 文件中。 </LI></UL>
<P>清单 3 展示了使用 Struts-Faces 标记的 FlightSearch.jsp。它类似于在 <A href="http://www-128.ibm.com/developerworks/cn/java/j-integrate/#listing1"><FONT color=#996699>清单 1</FONT></A>中展示的 JSF 例子。这里用粗体突出了区别之处。在这里，您会发现增加了一个新标记库 <I>tags-faces</I>。这个标记库定义声明这些标记由 Struts-Faces API 所使用。 </P><BR><A name=listing3><B>清单 3. FlightSearch.jsp 使用 Struts-Faces 标记</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %&gt;
&lt;%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %&gt;
&lt;%@ taglib uri="http://jakarta.apache.org/struts/tags-faces" 
  prefix="s" %&gt; 

&lt;f:use_faces&gt;
    &lt;s:form action="/listFlights"&gt;
     &lt;h:input_text id="fromCity" valueRef="FlightSearchForm.fromCity"/&gt;

     &lt;h:input_text id="toCity" valueRef="FlightSearchForm.toCity"/&gt;
     &lt;h:input_text id="departureDate" 
       valueRef="FlightSearchForm.departureDate"&gt;
     &lt;h:input_text id="arrivalDate" 
       valueRef="FlightSearchForm.arrivalDate"&gt;

     &lt;h:command_button id="submit" action="success" label="Submit" 
       commandName="submit" /&gt;
     &lt;h:command_button id="reset" action="reset" label="Reset" 
       commandName="reset" /&gt;

     &lt;s:errors/&gt;
    &lt;/s:form&gt;
&lt;/f:use_faces&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P><CODE>s:form</CODE> 标记用于创建这个 HTML 表单。表单的 action 属性是 <I>/listFlights</I>而不是像 <A href="http://www-128.ibm.com/developerworks/cn/java/j-integrate/#listing1"><FONT color=#996699>清单 1</FONT></A>那样指定为表单名 <I>flightForm</I>。在 JSF 中，表单名只是指定给 UIForm 的名字而没有更多的意义。 </P>
<P><CODE>FlightSearchBean</CODE> 是 JSF 表单的模型，并在更新模型值阶段得到其值。不过在 Struts 中，表单 action 指向 Struts 配置文件 struts-config.xml 中的 <CODE>ActionMapping</CODE> 。为了理解它是如何起作用的，还必须看一下清单 4 中显示的 struts-config.xml 文件。 </P>
<P>您会看到 <I>/listFlights</I> 的 <CODE>ActionMapping</CODE> 表明这个 URI 路径的 <CODE>ActionForm</CODE> 是 <CODE>foo.bar.FlightSearchForm</CODE> ，而 <CODE>Action</CODE> 类是 <CODE>foo.bar.FlightSearchAction</CODE> 。换句话说， <CODE>ActionForm</CODE> ( <CODE>FlightSearchForm</CODE> )本身就是 Struts-Faces 中的 HTML 表单的模型，它的 action 间接地指向这个模型(您可以在清单 3 中看到这一点，那里文本字段标记指向 <CODE>FlightSearchForm</CODE> 。在普通 Struts 应用程序中这会是 <CODE>&lt;html:text property="fromCity"/&gt;</CODE> )。 </P><BR><A name=listing4><B>清单 4. 在 struts-config.xml 中声明 Action</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;form-bean  name="FlightSearchForm"
               type="foo.bar.FlightSearchForm"/&gt;

&lt;!-- ========== Action Mapping Definition ========================= --&gt;
&lt;action-mappings&gt;

&lt;!-- List Flights action --&gt;
 &lt;action path="/listFlights"
    type="foo.bar.FlightSearchAction"
    name="FlightSearchForm"
    scope="request"
    input="/faces/FlightSearch.jsp"&gt;
    &lt;forward name="success" path="/faces/FlightList.jsp"/&gt;
 &lt;/action&gt;

&lt;/action-mappings&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>您会注意到在 action 属性中缺少熟悉的 <I>.do</I>。这是因为 Struts-Faces 使用表单 action 本身作为表单名(它还应该与 Struts 配置文件中的 <CODE>ActionForm</CODE> 名相匹配)。 </P>
<TABLE cellSpacing=0 cellPadding=0 width="40%" align=right border=0>
<TBODY>
<TR>
<TD width=10><IMG height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></TD>
<TD>
<TABLE cellSpacing=0 cellPadding=5 width="100%" border=1>
<TBODY>
<TR>
<TD bgColor=#eeeeee><A name=IDAJQDFG><B>集成 Struts 和 Tiles 的五个步骤</B></A><BR>
<P>以下五步可以让 Struts 1.1 和 Tiles 共同工作： <BR>1. 创建一个 JSP 以表示站点的布局。这是主 JSP，并带有页头、页体和页脚的占位符。分别用 Tiles 标记添加到主 JSP 页面中。 <BR>2. 创建一个 Tiles 定义文件并定义每个集成页面的每个占位符中必须包括哪个 JSP 页面。用惟一的名称标识出每一个合成页面定义。 <BR>3. 在 struts-config.xml 文件中改变全局和本地转发以使用上一步骤中给出的惟一名称而不是别名。 <BR>4. 在启动时用 <CODE>TilesPlugIn</CODE> 装载 Tiles 定义文件。将 <CODE>TilesPlugIn</CODE> 项加入到 struts-config.xml 文件中。 <BR>5. 将 <CODE>TilesRequestProcessor</CODE> 项添加到 struts-config.xml 文件中。这是支持 Tiles 的 Struts 应用程序的默认请求处理程序。 </P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<P>还要注意我们在这里没有使用 JSF <CODE>validation</CODE> 标记。这是因为在 Struts 中，验证是在 <CODE>ActionForm</CODE> 类中的 <CODE>validate()</CODE> <CODE>方法中进行的，有可能是通过使用 Commons-Validator。 <CODE>s:errors</CODE> 标记类似于 Struts 错误标记并用于显示在验证时出现的错误消息。 </P>
<P>另一件要注意的事情是没有 <CODE>ActionListener</CODE> 显式地与提交按钮相关联。这是因为在 Struts-Faces 中已经提供了 <CODE>ActionListener</CODE> 并且总是将 faces 请求与 <CODE>ActionEvent</CODE> s 一同转交给 <CODE>FacesRequestProcessor</CODE> ，在那里根据 struts-config.xml 文件将请求分派给相应的 <CODE>Action</CODE> 类。 </P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-integrate/#main"><B><FONT face=Verdana color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=4><SPAN class=atitle><FONT face=Arial size=4>将Struts 应用程序移植到 JSF</FONT></SPAN></A></P>
<P>为了将 Struts Web 应用程序与 JSF 集成，遵循以下步骤：</P>
<UL>
<LI>将 struts-faces.jar 文件与特定于 JSF 的 JAR(jsf-api.jar、jsf-ri.jar) 添加到 Web 应用程序的 <I>WEB-INF/lib</I>目录中。 <BR>
<LI>如果准备使用 JSF 和 JSTL，则将特定于 JSTL 的 JAR(jstl.jar、standard.jar)添加到 <I>WEB-INF/lib</I> 文件夹中。这一步只有在部署到常规 Tomcat 时才会需要。JWSDP 已经提供了这些 JAR。 <BR>
<LI>修改 Web 应用程序部署描述符 ( <I>/WEB-INF/web.xml</I>)以便有一个 Faces Servlet 项， 如清单 5 所示。 <BR>
<LI>修改 JSP 页面以使用 JSF 和 Struts-Faces 标记而不是 Struts 标记。特别是用 Struts-Faces 相应标记替换 <CODE>html、b</CODE> <CODE>ase、</CODE> <CODE>form</CODE> 和 <CODE>errors</CODE> 标记。用 JSF 相应标记替换 <CODE>text</CODE> 、 <CODE>textarea</CODE> 和 <CODE>radio</CODE> 标记。Struts-Faces 没有单独针对这些的标记。尽管没有要求，但是您可能还会考虑用 JSTL 标记替换 Struts Logic 标记。 <BR>
<LI>对于每一个使用 JSF 标记的 JSP，修改 struts-config.xml 文件以在指向该 JSP 的 Action Mapping 中的 <I>global-forwards</I>和 <I>local-forwards</I>中加入前缀 <I>/faces</I>。 <BR>
<LI>如果 Web 应用程序使用了任何您创建的自定义组件，那么您就需要用 JSF 实现的默认 RenderKit 注册它们。可以通过在 <I>WEB-INF</I> 文件中创建一个 faces-config.xml 文件、并增加每一个组件和 renderer 的项做到这一点。不过，要记住 faces-config.xml 文件已经绑定在 struts-faces.jar 文件中了。您必须从 struts-faces.jar 文件中提出它、加入自己的内容并将它放到 <I>WEB-INF</I>文件夹中。 </LI></UL><BR><A name=listing5><B>清单 5. 在 web.xml 中声明 FacesServlet</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;!-- JavaServer Faces Servlet Configuration --&gt;
&lt;servlet&gt;
&lt;servlet-name&gt;faces&lt;/servlet-name&gt;
&lt;servlet-class&gt;javax.faces.webapp.FacesServlet&lt;/servlet-class&gt;
&lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
&lt;/servlet&gt;

&lt;!-- JavaServer Faces Servlet Mapping --&gt;
&lt;servlet-mapping&gt;
  &lt;servlet-name&gt;faces&lt;/servlet-name&gt;
  &lt;url-pattern&gt;/faces/*&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><FONT face="Lucida Console"><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></FONT></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><FONT face="Lucida Console"><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></FONT></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-integrate/#main"><B><FONT face=Verdana color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=5><SPAN class=atitle><FONT face=Arial size=4>集成 Struts-Faces 和 Tiles 的挑战</FONT></SPAN></A></P>
<P>Struts-Faces 库提供了 Struts 与 JSF 之间的一个高效的桥梁，使得在 J2EE Web 应用程序中拥有丰富的表示层成为现实。您可以通过在组合体中添加 Titles 使表示层更丰富，这样不仅得到了 Struts 和 JSF 组合的好处，而且还可以高效地重复使用不同的 JSP 页面，因为它们将由可以根据需要添加或者删除的组件部分或者 tiles 所构成。</P>
<P>本文已经展示了 Struts 和 JSP 的集成，您会想将 Tiles 加入到组合中只是小事一桩，是不是？</P>
<P>不幸的是，JSF 仍然处于早期阶段，还没有给出最后的发布。基于这一考虑，Struts-Faces 集成软件开发仍然在不断地发展以包括 JSF 的不同的功能，并且还没有支持 Tiles。</P>
<P>Struts 和 Tiles 可以无缝地共同工作，但是在集成之路上您会遇到路障。在下面几小节中，您会看到在与 Tiles 共同使用 Struts-Faces 集成库时经常遇到的问题的汇总。对于每一个问题，我们详细说明了一个修改 Struts-Faces 类的解决方案。我们将用一个航班搜索示例解释这个解决方案。</P>
<P>清单 6 展示了航班搜索页面的布局。注意我们称它为航班搜索页面而不是 FlightSearch.jsp。这是因为 FlightSearch JSP 是用户在 foobar 旅行 Web 站点看到的合成页面的主体。</P>
<P>现在，我们保持实际的 FlightSearch.jsp 不变。我们将随着进展改变它。在您这边，也需要用航班搜索页的定义创建一个 Tiles 定义文件。清单 7(紧接着清单 6)展示了 Tiles 定义文件中航班搜索页的一项。注意对带有 <CODE>extends</CODE> 属性的主布局模板的重复使用。 </P>
<P>在清单 6 和 7 后是每一个可能的挑战。</P><BR><A name=listing6><B>清单 6. 航班搜索例子的 Tiles 布局</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %&gt;
&lt;%@ taglib uri="http://jakarta.apache.org/struts/tags-faces"prefix="s" %&gt;

&lt;!-- Layout component parameters: header, menu, body, footer --&gt;
&lt;s:html&gt;
&lt;head&gt;
  &lt;title&gt; &lt;tiles:getAsString name="title"/&gt;&lt;/title&gt;
  &lt;s:base/&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;TABLE border="0" width="100%" cellspacing="5"&gt;
    &lt;tr&gt;
     &lt;td&gt;&lt;tiles:insert attribute="header"/&gt;&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
     &lt;td&gt;&lt;tiles:insert attribute="body"/&gt;&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;&lt;td&gt;&lt;hr&gt;&lt;/td&gt;&lt;/tr&gt;

    &lt;tr&gt;
     &lt;td&gt;&lt;tiles:insert attribute="footer" /&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/TABLE&gt;
&lt;/body&gt;
&lt;/s:html&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR><BR><BR><A name=listing7><B>清单 7. 航班搜索页的 Tiles 定义</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;!-- Master Layout definition  --&gt;
&lt;definition name="foobar.master-layout" 
  path="/faces/layout/MasterLayout.jsp"&gt;
      &lt;put name="title"  value="Welcome to Foo Bar Travels" /&gt;
      &lt;put name="header" value="/faces/common/header.jsp" /&gt;
      &lt;put name="footer" value="/faces/common/footer.jsp" /&gt;
      &lt;put name="body"   value="" /&gt;
&lt;/definition&gt;

  &lt;!-- Definition for Flight Search Page --&gt;
&lt;definition name="/foobar.flight-search" 
  extends="foobar.master-layout"&gt;
      &lt;put name="body"   value="/faces/FlightSearch.jsp" /&gt;
&lt;/definition&gt;</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P><A name=N105D4><SPAN class=smalltitle><STRONG><FONT face=Arial>响应已经提交</FONT></STRONG></SPAN></A></P>
<P>这是您在试图访问航班搜索表单时马上会看到的第一个问题。小心查看堆栈跟踪。您会看到问题出在类 <CODE>com.sun.faces.lifecycle.ViewHandlerImpl</CODE> 上。这是一个实现了 <CODE>ViewHandler</CODE> 接口的 JSF-RI 类。 </P>
<P><A href="http://www-128.ibm.com/developerworks/cn/java/j-integrate/#fig2"><FONT color=#996699>图 2</FONT></A>展示了 <CODE>ViewHandler</CODE> 所扮演的角色。这是一个将请求转发给下一页的类。在转发请求时，它不在转发前检查响应的状态 -- 这只有在使用 Tiles 时才会发生，因为 Tiles 内部将 JSP 页面包括在响应内，而 JSF-RI 在第一次转发后提交响应、然后试图再次转发给下面的包括 JSP 的 Tiles。 </P>
<P>要解决这个问题，必须创建一个自定义的 <CODE>ViewHandler</CODE> 实现，它将检查响应的状态以确定它是否提交过。如果响应没有提交过，那么请求就转发给下一页，否则，就加入请求并显示相应的 JSP。我们将创建一个名为 <CODE>STFViewHandlerImpl</CODE> 的类，它实现了 <CODE>ViewHandler</CODE> 接口并实现了所需要的方法 <CODE>renderView()。</CODE> 清单 8 展示了 <CODE>STFViewHandlerImpl</CODE> 中的 <CODE>renderView()</CODE> 方法： </P><BR><A name=listing8><B>清单 8. STFViewHandlerImpl 中的 renderView()方法</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">RequestDispatcher rd = null;
Tree tree = context.getTree();
String requestURI = context.getTree().getTreeId();
rd = request.getRequestDispatcher(requestURI);

/** If the response is committed, include the resource **/
if( !response.isCommitted() ) {
   rd.forward(request, context.getServletResponse());
}
else {
   rd.include(request, context.getServletResponse());
}</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>现在您实现了自己的 <CODE>ViewHandler</CODE> ，如何通知 JSF-RI 使用您的 <CODE>ViewHandler</CODE> 而不是默认的实现呢？要回答这个问题，就必须理解 <CODE>FacesServlet</CODE> 的工作过程。 </P>
<P>在 Faces 初始化过程中， <CODE>FacesServlet</CODE> 会让 <CODE>LifecycleFactory</CODE> 实现返回 <CODE>Lifecycle</CODE> 类的一个实现，如清单 9 所示： </P><BR><A name=listing9><B>清单 9. FacesServlet 中 Faces 的初始化</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">//Get the LifecycleFactory from the Factory Finder
LifecycleFactory factory = (LifecycleFactory) 
  FactoryFinder.getFactory("javax.faces.lifecycle.LifecycleFactory");

//Get the context param from web.xml
String lifecycleID = 
getServletContext().getInitParameter("javax.faces.lifecycle.LIFECYCLE_ID");

//Get the Lifecycle Implementation
Lifecycle lifecycle = factory.getLifecycle(lifeCycleID);
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P><CODE>Lifecycle</CODE> 实现对象拥有在呈现响应阶段要使用的 <CODE>ViewHandler</CODE> 。您可以通过对 <CODE>Lifecycle</CODE> 实现调用 <CODE>setViewHandler</CODE> 方法让自己的 <CODE>ViewHandler</CODE> 实现成为默认的。 </P>
<P>现在问题变为如何得到默认 <CODE>Lifecycle</CODE> 实现？回答是不需要这样做。只要创建一个新的实现并用一个惟一 ID 注册它，如清单 10 所示： </P><BR><A name=listing10><B>清单 10. 注册自定义 ViewHandler 和 Lifecycle</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">//Get the LifecycleFactory from the Factory Finder
LifecycleFactory factory = (LifecycleFactory) 
  FactoryFinder.getFactory("javax.faces.lifecycle.LifecycleFactory");

//Create a new instance of Lifecycle implementation - 
//com.sun.faces.lifecycle.LifecycleImpl
//According to the documentation, factory.getLifecycle("STFLifecycle") 
//should work, but JSF-RI has a defect.
//Hence this workaround of creating a RI class explicitly.
LifecycleImpl stfLifecycleImpl = new LifecycleImpl();

//Create a new instance of our STFViewHandler and set it on the Lifecycle
stfLifecycleImpl.setViewHandler(new STFViewHandlerImpl());

//Register the new lifecycle with the factory with a unique 
//name "STFLifecycle"
factory.addLifecycle("STFLifecycle", stfLifecycleImpl);
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>您可以看到 <CODE>lifecycleId</CODE> 硬编码为 <CODE>STFLifecycle</CODE> 。实际上不是这样。当您回过头分析 <A href="http://www-128.ibm.com/developerworks/cn/java/j-integrate/#listing9"><FONT color=#996699>清单 9</FONT></A>时就会清楚。 <CODE>FacesServlet</CODE> 从在 web.xml 文件中声明的上下文参数中得到名为 <CODE>javax.faces.lifecycle.LIFECYCLE_ID</CODE> 的 lifecycle ID，如下所示： </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">    &lt;context-param&gt;
        &lt;param-name&gt;javax.faces.lifecycle.LIFECYCLE_ID&lt;/param-name&gt;
        &lt;param-value&gt;STFLifecycle&lt;/param-value&gt;
    &lt;/context-param&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>因为 <CODE>FacesServlet</CODE> 取决于其初始化时的 <CODE>Lifecycle</CODE> 实现，在 <A href="http://www-128.ibm.com/developerworks/cn/java/j-integrate/#listing10"><FONT color=#996699>清单 10</FONT></A>中展示的代码应该在 <CODE>FacesServlet</CODE> 初始化之前执行。通过创建另一个 servlet 并在 <CODE>FacesServlet</CODE> 之前初始化它而做到这一点。 </P>
<P>但是一种更聪明的办法是实现一个 <CODE>ServletContextListener</CODE> 接口。这个类声明两个方法： <CODE>contextInitialized()</CODE> 和 <CODE>contextDestroyed()</CODE> ，在 Web 应用程序被创建及 Web 应用程序被销毁之前会分别调用它们。因而 <A href="http://www-128.ibm.com/developerworks/cn/java/j-integrate/#listing10"><FONT color=#996699>清单 10</FONT></A>中的代码在 <CODE>contextInitialized()</CODE> 方法中执行，而自定义 <CODE>ViewHandler</CODE> 已经用标识名 <CODE>STFLifecycle</CODE> 注册到 <CODE>Lifecycle</CODE> ，并且可被 <CODE>FacesServlet</CODE> 使用。 <CODE>ServletContextListener</CODE> 类本身是在 web.xml 文件中声明的，如下所示： </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;listener&gt;
  &lt;listener-class&gt;foo.bar.stf.application.STFContextListener
  &lt;/listener-class&gt;
&lt;/listener&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>这不是注册一个带有自定义 <CODE>ViewHandler</CODE> 的 <CODE>Lifecycle</CODE> 惟一方法。事实上 <CODE>FactoryFinder</CODE> 实现了自己的发现算法以发现 <CODE>Factory</CODE> 对象，包括 <CODE>LifecycleFactory</CODE> 。这些机制按照顺序包括在系统属性中查看工厂实现类名的机制、faces.properties file、或者 1.3 Services 发现机制( <CODE>META-INF/services/{factory-class-name}</CODE> )。不过，我们讨论的这种机制是最容易的，也是最不具有破坏性的一种。 </P>
<P><A name=N106E1><SPAN class=smalltitle><STRONG><FONT face=Arial>404 Resource Not Found</FONT></STRONG></SPAN></A></P>
<P>在解决了提交响应的问题后，单击任何一个 Tiles 特定的链接或者输入一个会呈现 Faces 响应的 URL。在这里，可以输入显示 <CODE>FlightSearchForm</CODE> 的 URL。 </P>
<P>在这样做了以后，您会得到一个 <I>foobar.flight-search - 404 Resource Not Found</I> 错误。 <CODE>foobar.flight-search</CODE> 是航班搜索页面的 Tiles 定义的名字。 <CODE>FacesRequestProcessor</CODE> 不能处理 Tiles 请求(因为它扩展的是 <CODE>RequestProcessor</CODE> 而不是 <CODE>TilesRequestProcessor</CODE> )，所以会得到错误。 </P>
<P>为解决这个问题，我们将创建一个名为 <CODE>STFRequestProcessor</CODE> (表示 <I>Struts-Tiles-Faces Request Processor</I>)的新的请求处理程序。现在我们将拷贝 <CODE>FacesRequestProcessor</CODE> 的所有代码到这个新类中。惟一的区别是 <CODE>STFRequestProcessor</CODE> 继承的是 <CODE>TilesRequestProcessor</CODE> 而不是继承常规的 <CODE>RequestProcessor</CODE> 。这个新的 <CODE>RequestProcessor</CODE> 可以处理 Tiles 请求。清单 11 详细列出了这个 <CODE>STFRequestProcessor</CODE> ： </P><BR><A name=listing11><B>清单 11. STFRequestProcessor.java</B></A><BR>
<P>正如您所知道的， <CODE>Struts</CODE> 框架的 <CODE>RequestProcessor</CODE> 是在 struts-config.xml 文件中指定的。将下面的项添加到 <CODE>struts-cinfig.xml </CODE>文件中后， <CODE>STFRequestProcessor</CODE> 就成为处理程序： </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;controller processorClass="foobar.stf.application.STFRequestProcessor" /&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P><A name=N10743><SPAN class=smalltitle><STRONG><FONT face=Arial>表单提交显示返回同一个表单</FONT></STRONG></SPAN></A></P>
<P>由于 <CODE>STFRequestProcessor</CODE> 的作用，这时您就可以浏览并查看航班页面了。不过，在提交航班搜索表单时，您会得到返回来的同一个表单，而且没有页头和页脚！并且没有验证错误。事实上，根本就没有进行验证！ </P>
<P>为了了解到底发生了什么事情，我们用浏览器回到航班页面并检查 HTML 源代码。您会看到像下面这样的一项：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;form name="FlightSearchForm" method="post" 
  action="/flightapp/faces/FlightSearch.jsp"&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>注意表单 action 是指向 JSP 页而不是一个 <I>.do</I> 的。啊哈！这就是问题！这不是由于同时使用 Tiles 和 Struts-Faces 而带来的新问题，Struts-Faces 的默认行为是让 JSP 与表单 action 有同样的名字。这种行为在有单一的 JSP 页(如在前面的 Struts-Faces 例子中)时没有问题。 <A href="http://www-128.ibm.com/developerworks/cn/java/j-integrate/#listing3"><FONT color=#996699>清单 3</FONT></A>展示了原来的 FlightSearch.jsp，让我们继续并像下面这样修改 action： </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;s:form action="/listFlights.do&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>当然，光有这种修改并不能解决问题。作了这种改变后，您就会发现 <CODE>STFRequestProcessor</CODE> 不能找到 <CODE>ActionForm</CODE> 。显然还需要其他的改变。 </P>
<P>不过，在继续往下之前，看一下图&amp;#160 5。它显示了在呈现负责 Struts-Faces 表单的 faces 时相关的一系列事件。这与 <A href="http://www-128.ibm.com/developerworks/cn/java/j-integrate/#fig3"><FONT color=#996699>图 3</FONT></A>相同，除了在 <CODE>FormComponent</CODE> 中突出显示的方法 <CODE>createActionForm()。</CODE> 由 Struts-Faces API 提供的 <CODE>FormComponent</CODE> 类是 <CODE>javax.faces.component.UIForm</CODE> 的特殊子类，它支持请求或者会话范围的表单 Bean。 </P>
<P><B>图 5. 呈现 Struts-Faces 响应</B> <BR><A href="http://www.ibm.com/developerworks/java/library/j-integrate/side-fig5.html"><FONT color=#5c81a7>单击这里</FONT></A>以查看该图。 </P>
<P>正如您所看到的， <CODE>createActionForm()</CODE> 方法使用 action 名以从 Struts 配置文件中得到 <CODE>ActionMapping</CODE> 。因为没有对于 <I>/listFlights.do</I> 的 <CODE>ActionMapping</CODE> ，所以 Struts 不能找到 <CODE>ActionForm。</CODE> </P>
<P>这个问题的解决方法是使用 <CODE>org.apache.struts.util.RequestUtils</CODE> 。 <CODE>RequestUtils</CODE> 中的 static 方法 <CODE>getActionMappingName()</CODE> 具有足够的智能解析映射到正确 <CODE>ActionMapping</CODE> 的路径( <I>/x/y/z</I>)或者后缀( <I>.do</I>)。 </P>
<P>清单 12 以粗体显示对 <CODE>createActionForm</CODE> 方法的改变。我们没有对 Struts-Faces 中的 <CODE>FormComponent</CODE> 作这些改变，而是通过继承 <CODE>FormComponent</CODE> 并覆盖 <CODE>createActionForm() </CODE>方法创建了一个新的 <CODE>STFFormComponent。</CODE> </P><BR><A name=listing12><B>清单 12. FormComponent 中修改过的 createActionForm() 方法</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">// Look up the application module configuration information we need
ModuleConfig moduleConfig = lookupModuleConfig(context);

// Look up the ActionConfig we are processing
String action = getAction();
String mappingName = RequestUtils.getActionMappingName(action);
ActionConfig actionConfig = moduleConfig.findActionConfig(mappingName);
....
....
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>对新的 <CODE>STFFormComponent</CODE> 还要作一项改变。Struts-Faces 将 action 名本身作为表单名。这需要改变，因为 action 带有后缀 <I>.do</I>，而表单名没有后缀 <I>.do</I>。所以我们在 <CODE>STFFormComponent</CODE> 上增加一个名为 <CODE>action</CODE> 的新属性，并覆盖 <CODE>getAction()</CODE> 和 <CODE><CODE>setAction()</CODE> 方法。 </P>
<P><A name=N10803><SPAN class=smalltitle><STRONG><FONT face=Arial>FormRenderer 的改变</FONT></STRONG></SPAN></A></P>
<P>必须对 <CODE>FormRenderer</CODE> (以 HTML 格式呈现 Struts-Faces 表单的类)的 <CODE>encodeBegin</CODE> 方法进行类似于 <A href="http://www-128.ibm.com/developerworks/cn/java/j-integrate/#listing10"><FONT color=#996699>清单 10</FONT></A>所示的修改。 </P>
<P>同样，通过继承 <CODE>FormRenderer</CODE> 做到这一点。此外，还必须改变写出到 HTML 的表单 action。清单 13以粗体详细列出了这些改变： </P><BR><A name=listing13><B>清单 13. FormRenderer 的改变</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">protected String action(FacesContext context, UIComponent component) {

    String treeId = context.getTree().getTreeId();
    StringBuffer sb = new StringBuffer 
      (context.getExternalContext().getRequestContextPath());
    sb.append("/faces");

    // sb.append(treeId); -- This is old code, replaced with 
    // the two lines below.

    STFFormComponent fComponent = (STFFormComponent) component;
    sb.append(fComponent.getAction());
    
    return (context.getExternalContext().encodeURL(sb.toString()));
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P><B>FormTag</B>的改变 <BR>正如您已经知道的，当组件和 renderer 改变时，标记也必须改变。在这里，通过继承 Struts-Faces 中的 <CODE>FormTag</CODE> 创建一个新的标记： <CODE>STFFormTag</CODE> 。不必改变任何功能，只要覆盖 <CODE>getComponentType()</CODE> 和 <CODE>getRendererType()</CODE> 方法。清单 14 展示了从 <CODE>STFFormComponent</CODE> 覆盖的方法： </P><BR><A name=listing14><B>清单 14. FormTag 的改变</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">public String getComponentType()
{
    return ("STFFormComponent");
}

public String getRendererType()
{
    return ("STFFormRenderer");
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P><A name=N1084F><SPAN class=smalltitle><STRONG><FONT face=Arial>修改 faces-config.xml 文件</FONT></STRONG></SPAN></A></P>
<P>自定义组件和 renderer 必须在 faces-config.xml 文件中声明，这样 JSF 框架才可以初始化并使用它们。现在我们已经创建了一个新组件 <CODE>STFFormComponent</CODE> 和一个新 renderer <CODE>STFFormRenderer</CODE> 。 </P>
<P>现在我们将在 faces-config.xml 文件中增加一个声明，如清单 15 所示。 <I>component-class</I> <I>是组件的完全限定类名。 <I>component-type</I> 指的是在 <CODE>STFFormTag</CODE> ( <A href="http://www-128.ibm.com/developerworks/cn/java/j-integrate/#listing12"><FONT color=#996699>清单 12</FONT></A>)中用于标识组件的名字。以类似的方式发现和解释 renderer。注意 faces-config.xml 文件是在 struts-faces.jar 文件中的。从 struts-faces.jar 文件中取出这个文件并将它放到 Web 应用程序的 <I>WEB-INF</I>文件夹中并修改它。 </P><BR><A name=listing15><B>清单 15. 在 faces-config.xml 中声明自定义组件和 renderer</B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;faces-config&gt;

  &lt;!-- Custom Components --&gt;
  &lt;component&gt;
    &lt;component-type&gt;STFFormComponent&lt;/component-type&gt;
    &lt;component-class&gt;
      foobar.stf.component.STFFormComponent
    &lt;/component-class&gt;
  &lt;/component&gt;
  ..
  ..
  ..
  &lt;!-- Custom Renderers --&gt;
  &lt;render-kit&gt;

    &lt;renderer&gt;
      &lt;renderer-type&gt;STFFormRenderer&lt;/renderer-type&gt;
      &lt;renderer-class&gt;
        foobar.stf.renderer.STFFormRenderer
      &lt;/renderer-class&gt;
    &lt;/renderer&gt;
    ..
    ..
    ..
  &lt;/render-kit&gt;
&lt;/faces-config&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P><A name=N10880><SPAN class=smalltitle><STRONG><FONT face=Arial>修改 struts-faces.tld 文件</FONT></STRONG></SPAN></A></P>
<P>您不会在这个示例 Struts-Faces 应用程序中看到 struts-faces.tld 文件，它打包到了 struts-faces.jar 文件中。打开并分析这个文件。它声明了一个名为 <CODE>org.apache.struts.faces.taglib.LifecycleListener</CODE> 的类，这个类实现了 <CODE>ServletContextListener</CODE> 并初始化 <CODE>FacesRequestProcessor</CODE> 。 </P>
<P>因为希望使用新的 <CODE>STFRequestProccessor</CODE> ，所以必须将这个文件从 struts-faces.jar 文件中删除，将它放到 Web 应用程序的 <I>WEB-INF</I> 文件夹中，并删除侦听器声明。如果让这个 tld 文件保持原样，那么在初始化这个 Web 应用程序时，除了 <CODE>STFRequestProcessor</CODE> ，还会实例化一个 <CODE>FacesRequestProcessor。</CODE> </P>
<P><A name=IDA4JEFG>修改 base href 标记 <BR>现在，您已经完成了 Struts、Tiles、JSF 集成的最困难的部分了。您甚至可以浏览航班搜索页面，并输入搜索标准查看航班列表。现在试着从航班列表页面返回航班搜索表单。您会得到一个 HTTP 400 错误。这个错误的原因是 HTML <CODE>base href</CODE> 标记。它被设置为 Master Layout 页面。 </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;base href=
  "http://localhost:8080/stf-example/faces/layout/MasterLayout.jsp" /&gt;
           |_________|       |_____________________|
              Context               Servlet Path
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>程序所有页面浏览都是相对于布局页面计算的。如果加入的 <CODE>base href</CODE> 标记只达到 Web 应用程序上下文则会很方便，像这样： </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">&lt;base href="http://localhost:8080/stf-example/" /&gt;
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR>
<P>我们可以通过定制 Struts-Faces <CODE>BaseTag</CODE> 做到这一点。这个类中的改变相当微不足道。只须在 base href 中去掉 <CODE>HttpServletRequest.getServletPath()</CODE> 。 </P>
<P>因为这些改变是与显示相关的，所以为它创建了一个名为 <CODE>STFBaseRenderer</CODE> 的新 renderer。这个新标记称为 <CODE>STFBaseTag</CODE> ，它声明 <CODE>STFBaseRenderer</CODE> 作为其关联的 renderer。不需要新的组件。 </P>
<P>有了这些信息，通过继承 <CODE>BaseTag</CODE> 并覆盖 <CODE>getRendererType</CODE> 方法创建新的 <CODE>STFBaseTag</CODE> ，如下所示： </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
<TBODY>
<TR>
<TD><PRE><CODE class=section>
<FONT face="Lucida Console">public String getRendererType()
{
    return ("STFBaseRenderer");
}
</FONT></CODE></PRE></TD></TR></TBODY></TABLE><BR><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><FONT face="Lucida Console"><IMG height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"></FONT></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><FONT face="Lucida Console"><IMG height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><BR></FONT></TD>
<TD vAlign=top align=right><A class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-integrate/#main"><B><FONT face=Verdana color=#996699>回页首</FONT></B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=6><SPAN class=atitle><FONT face=Arial size=4>到目前为止所作的改变</FONT></SPAN></A></P>
<P>恭喜！经过这些相对较小的修改，您已经成功地集成了 Struts、Tiles 和 JSF，并保留了您以前在这些技术上所做的所有投资。本文演示了如何将 JSF 强大的前端能力、 Tiles 的内容格式编排优势以及 Struts 控制器层的灵活性结合在一个包中，使得创建一个 J2EE Web 应用程序成为一项更容易的任务。</P>
<P>我们讨论了定制 Struts 类以便与 JavaServer Faces 和 Tiles 框架形成紧密集成的工作关系，包括下面这些修改和增加：</P>
<UL>
<LI>新的 <CODE>ViewHandler</CODE> ，用于检查提交的响应。 <BR>
<LI>新的 <CODE>ServletContextListener</CODE> ，用于创建新的 <CODE>Lifecycle</CODE> 实现并注册这个定制的 <CODE>ViewHandler。</CODE> <BR>
<LI>新的 <CODE>RequestProcessor</CODE> ，用于处理 Tiles 请求。 <BR>
<LI>修改过的 web.xml 文件，声明新的 <CODE>ServletContextListener</CODE> 和 JSF Lifecycle ID。 <BR>
<LI>新的 <CODE>FormTag、</CODE> <CODE>FormComponent</CODE> 和 <CODE>FormRenderer</CODE> 类。 <BR>
<LI>新的 <CODE>BaseTag</CODE> 和 <CODE>BaseRenderer</CODE> 类。 <BR>
<LI>修改过的 faces-config.xml 文件，它声明了新的组件和 renderer。 
<LI>修改过的 struts-faces.tld 文件，不声明侦听器。 </LI></UL>
<P>希望它可以概括本文中使用的复合技术，最重要的是，我们为您提供了将 Struts、Tiles 和 JavaServer Faces 结合到用于构建 Web 应用程序的一个强大而灵活的机制中的一个令人信服的路线图。<BR><A HREF="/Files/zlsunnan/JSF_Examples.rar"><STRONG><FONT size=6>代码下载</FONT></STRONG></A></P></I></CODE></CODE></CODE><img src ="http://www.blogjava.net/zlsunnan/aggbug/22939.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zlsunnan/" target="_blank">☆蓝色梦想☆</a> 2005-12-08 09:26 <a href="http://www.blogjava.net/zlsunnan/archive/2005/12/08/22939.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>