﻿<?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-table-文章分类-struts</title><link>http://www.blogjava.net/table/category/43261.html</link><description /><language>zh-cn</language><lastBuildDate>Sat, 13 Mar 2010 16:49:41 GMT</lastBuildDate><pubDate>Sat, 13 Mar 2010 16:49:41 GMT</pubDate><ttl>60</ttl><item><title>使用 Struts 2 开发 RESTful 服务</title><link>http://www.blogjava.net/table/articles/315146.html</link><dc:creator>小卓</dc:creator><author>小卓</author><pubDate>Thu, 11 Mar 2010 03:06:00 GMT</pubDate><guid>http://www.blogjava.net/table/articles/315146.html</guid><wfw:comment>http://www.blogjava.net/table/comments/315146.html</wfw:comment><comments>http://www.blogjava.net/table/articles/315146.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/table/comments/commentRss/315146.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/table/services/trackbacks/315146.html</trackback:ping><description><![CDATA[<p>级别： 初级</p>
<p><a href="http://www.ibm.com/developerworks/cn/java/j-lo-struts2rest/?ca=drs-tp4608#author">李 刚</a>, 自由撰稿人<br />
</p>
<p>2009 年 8 月 28 日</p>
<blockquote>从 V2.1 开始，Struts 2 开始提供 Convention 插件，它允许根据&#8220;约定&#8221;来搜索 Action，以及管理 Action 和 Result 的映射。另外，Struts 2.1 还提供了 REST 插件，使 Struts 2 可以支持 Rails 风格的 URL，以对外提供 REST 风格的资源服务。本文作者通过代码示例演示了这些特性。</blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end RESERVED FOR FUTURE USE INCLUDE FILES-->
<p><a name="major1"><span class="atitle">REST 简介</span></a></p>
<p>REST 是英文 Representational State Transfer 的缩写，这个术语由 Roy Thomas Fielding 博士在他的论文《Architectural Styles and the Design of Network-based Software Architectures》中提出。从这篇论文的标题可以看出：REST 是一种基于网络的软件架构风格。</p>
<p>提示：国内很多网络资料将 REST 翻译为&#8220;表述性状态转移&#8221;，不过笔者对这个翻译不太认同。因为这个专业术语无法传达 REST 的含义，读者可以先不要理会 REST 到底该如何翻译，尽量先去理解 REST 是什么？有什么用？然后再来看这个术语的翻译。关于 Roy Thomas Fielding 博士的原文参见如下地址：http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm。</p>
<p>REST 架构是针对传统 Web 应用提出的一种改进，是一种新型的分布式软件设计架构。对于异构系统如何进行整合的问题，目前主流做法都集中在使用 SOAP、WSDL 和 WS-* 规范的 Web Services。而 REST 架构实际上也是解决异构系统整合问题的一种新思路。</p>
<p>如果开发者在开发过程中能坚持 REST 原则，将可以得到一个使用了优质 Web 架构的系统，从而为系统提供更好的可伸缩性，并降低开发难度。关于 REST 架构的主要原则如下：</p>
<ul>
    <li>网络上的所有事物都可被抽象为资源（Resource）。
    <li>每个资源都有一个唯一的资源标识符（Resource Identifier）。
    <li>同一资源具有多种表现形式。
    <li>使用标准方法操作资源。
    <li>通过缓存来提高性能。
    <li>对资源的各种操作不会改变资源标识符。
    <li>所有的操作都是无状态的（Stateless）。 </li>
</ul>
<p>仅从上面几条原则来看 REST 架构，其实依然比较难以理解，下面笔者将从如下二个方面来介绍 REST。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="41" sizcache="2">
    <tbody sizset="41" sizcache="1">
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right" sizset="42" sizcache="2">
    <tbody sizset="43" sizcache="2">
        <tr align="right" sizset="43" sizcache="2">
            <td sizset="43" sizcache="2"><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0" sizset="43" sizcache="2">
                <tbody sizset="43" sizcache="1">
                    <tr>
                        <td valign="middle"><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.ibm.com/developerworks/cn/java/j-lo-struts2rest/?ca=drs-tp4608#main"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="major2"><span class="atitle">资源和标识符</span></a></p>
<p>现在的 Web 应用上包含了大量信息，但这些信息都被隐藏在 HTML、CSS 和 JavaScript 代码中，对于普通浏览者而言，他们进入这个系统时无法知道该系统包含哪些页面；对于一个需要访问该系统资源的第三方系统而言，同样无法明白这个系统包含多少功能和信息。</p>
<table cellspacing="0" cellpadding="0" width="40%" align="right" border="0" sizset="44" sizcache="2">
    <tbody sizset="45" sizcache="2">
        <tr sizset="45" sizcache="2">
            <td width="10"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" /></td>
            <td sizset="45" sizcache="2">
            <table cellspacing="0" cellpadding="5" width="100%" border="1" sizset="45" sizcache="2">
                <tbody sizset="45" sizcache="1">
                    <tr>
                        <td bgcolor="#eeeeee"><a name="sidebar1"><strong>URI 和 URL</strong></a><br />
                        <p>与 URI 相关的概念还有 URL，URL 是 Uniform Resource Locator，也就是统一资源定位符的意思。其中 http://www.crazyit.org 就是一个统一资源定位符，URL 是 URI 的子集。简而言之：每个 URL 都是 URI，但不是每个 URI 都是 URL。</p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<p>从 REST 架构的角度来看，该系统里包含的所有功能和信息，都可被称为资源（Resource），REST 架构中的资源包含静态页面、JSP 和 Servlet 等，该应用暴露在网络上的所有功能和信息都可被称为资源。</p>
<p>除此之外，REST 架构规范了应用资源的命名方式，REST 规定对应用资源使用统一的命名方式：REST 系统中的资源必须统一命名和规划，REST 系统由使用 URI（Uniform Resource Identifier，即统一资源标识符）命名的资源组成。由于 REST 对资源使用了基于 URI 的统一命名，因此这些信息就自然地暴露出来了，从而避免 &#8220;信息地窖&#8221;的不良后果。</p>
<p>对于当今最常见的网络应用来说，资源标识符就是 URI，资源的使用者则根据 URI 来操作应用资源。当 URI 发生改变时，表明客户机所使用的资源发生了改变。</p>
<p>从资源的角度来看，当客户机操作不同的资源时，资源所在的 Web 页（将 Web 页当成虚拟的状态机来看）的状态就会发生改变、迁移（Transfer），这就是 REST 术语中 ST（State Tranfer）的由来了。</p>
<p>客户机为了操作不同状态的资源，则需要发送一些 Representational 的数据，这些数据包含必要的交互数据，以及描述这些数据的元数据。这就是 REST 术语中 RE（Representational）的由来了。理解了这个层次之后，至于 REST 如何翻译、或是否真正给它一个中文术语，读者可自行决定。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="46" sizcache="2">
    <tbody sizset="46" sizcache="1">
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right" sizset="47" sizcache="2">
    <tbody sizset="48" sizcache="2">
        <tr align="right" sizset="48" sizcache="2">
            <td sizset="48" sizcache="2"><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0" sizset="48" sizcache="2">
                <tbody sizset="48" sizcache="1">
                    <tr>
                        <td valign="middle"><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.ibm.com/developerworks/cn/java/j-lo-struts2rest/?ca=drs-tp4608#main"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="major3"><span class="atitle">操作资源的方式</span></a></p>
<p>对于 REST 架构的服务器端而言，它提供的是资源，但同一资源具有多种表现形式（可通过在 HTTP Content-type 头中包含关于数据类型的元数据）。如果客户程序完全支持 HTTP 应用协议，并能正确处理 REST 架构的标准数据格式，那么它就可以与世界上任意一个 REST 风格的用户交互。这种情况不仅适用于从服务器端到客户端的数据，反之亦然——倘若从客户端传来的数据符合 REST 架构的标准数据格式，那么服务器端也可以正确处理数据，而不去关心客户端的类型。</p>
<p>典型情况下，REST 风格的资源能以 XHTML、XML 和 JSON 三种形式存在，其中 XML 格式的数据是 WebServices 技术的数据交换格式，而 JSON 则是另一种轻量级的数据交换格式；至于 XHTML 格式则主要由浏览器负责呈现。当服务器为所有资源提供多种表现形式之后，这些资源不仅可以被标准 Web 浏览器所使用，还可以由 JavaScript 通过 Ajax 技术调用，或者以 RPC（Remote Procedure Call）风格调用，从而变成 REST 风格的 WebServices。</p>
<p>REST 架构除了规定服务器提供资源的方式之外，还推荐客户端使用 HTTP 作为 Generic Connector Interface（也就是通用连接器接口），而 HTTP 则把对一个 URI 的操作限制在了 4 个之内：GET、POST、PUT 和 DELETE。通过使用通用连接器接口对资源进行操作的好处是保证系统提供的服务都是高度解耦的，从而简化了系统开发，改善了系统的交互性和可重用性。</p>
<p>REST 架构要求客户端的所有的操作在本质上是无状态的，即从客户到服务器的每个 Request 都必须包含理解该 Request 的所有必需信息。这种无状态性的规范提供了如下几点好处：</p>
<ul>
    <li>无状态性使得客户端和服务器端不必保存对方的详细信息，服务器只需要处理当前 Request，而不必了解前面 Request 的历史。
    <li>无状态性减少了服务器从局部错误中恢复的任务量，可以非常方便地实现 Fail Over 技术，从而很容易地将服务器组件部署在集群内。
    <li>无状态性使得服务器端不必在多个 Request 中保存状态，从而可以更容易地释放资源。
    <li>无状态性无需服务组件保存 Request 状态，因此可让服务器充分利用 Pool 技术来提高稳定性和性能。 </li>
</ul>
<p>当然，无状态性会使得服务器不再保存 Request 的状态数据，因此需要在一系列 Request 中发送重复数据，从而提高了系统的通信成本。为了改善无状态性带来的性能下降，REST 架构填加了缓存约束。缓存约束允许隐式或显式地标记一个 Response 中的数据，这样就赋予了客户端缓存 Response 数据的功能，这样就可以为以后的 Request 共用缓存的数据，部分或全部的消除一些交互，增加了网络的效率。但是用于客户端缓存了信息，也就同时增加了客户端与服务器数据不一致的可能，从而降低了可靠性。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="49" sizcache="2">
    <tbody sizset="49" sizcache="1">
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right" sizset="50" sizcache="2">
    <tbody sizset="51" sizcache="2">
        <tr align="right" sizset="51" sizcache="2">
            <td sizset="51" sizcache="2"><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0" sizset="51" sizcache="2">
                <tbody sizset="51" sizcache="1">
                    <tr>
                        <td valign="middle"><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.ibm.com/developerworks/cn/java/j-lo-struts2rest/?ca=drs-tp4608#main"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="major4"><span class="atitle">Struts 2 的 REST 支持</span></a></p>
<table cellspacing="0" cellpadding="0" width="40%" align="right" border="0" sizset="52" sizcache="2">
    <tbody sizset="53" sizcache="2">
        <tr sizset="53" sizcache="2">
            <td width="10"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" /></td>
            <td sizset="53" sizcache="2">
            <table cellspacing="0" cellpadding="5" width="100%" border="1" sizset="53" sizcache="2">
                <tbody sizset="53" sizcache="1">
                    <tr>
                        <td bgcolor="#eeeeee"><a name="sidebar2"><strong>约定优于配置</strong></a><br />
                        <p>Convention 这个单词的翻译过来就是&#8220;约定&#8221;的意思。有 Ruby On Rails 开发经验的读者知道 Rails 有一条重要原则：约定优于配置。Rails 开发者只需要按约定开发 ActiveRecord、ActiveController 即可，无需进行配置。很明显，Struts 2 的 Convention 插件借鉴了 Rails 的创意，甚至连插件的名称都借鉴了&#8220;约定优于配置&#8221;原则。</p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<p>从 Struts 2.1 开始，Struts 2 改为使用 Convention 插件来支持零配置。Convention 插件彻底地抛弃了配置信息，不仅不需要使用 struts.xml 文件进行配置，甚至不需要使用 Annotation 进行配置。而是由 Struts 2 根据约定来自动配置。</p>
<p>Convention 这个单词的翻译过来就是&#8220;约定&#8221;的意思。有 Ruby On Rails 开发经验的读者知道 Rails 有一条重要原则：约定优于配置。Rails 开发者只需要按约定开发 ActiveRecord、ActiveController 即可，无需进行配置。很明显，Struts 2 的 Convention 插件借鉴了 Rails 的创意，甚至连插件的名称都借鉴了&#8220;约定优于配置&#8221;原则。</p>
<p>由于 Struts 2 的 Convention 插件的主要特点是&#8220;约定优于配置&#8221;，当我们已经习惯了 Struts 2 的基本开发方法之后，如果希望改为使用 Convention 插件也非常容易，我们只要放弃 Stuts 2.1 应用原有的配置文件，改为按 Convention 插件的约定来定义 Action 即可。</p>
<p>以 Convention 插件为基础，Struts 2.1 又新增了 REST 插件，允许 Struts 2 应用对外提供 REST 服务。REST 插件也无需使用 XML 进行配置管理。Struts 2.1 通过 REST 插件完全可以提供让人和机器客户端共同使用的资源，并支持 Ruby On Rails 风格的 URL。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="54" sizcache="2">
    <tbody sizset="54" sizcache="1">
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right" sizset="55" sizcache="2">
    <tbody sizset="56" sizcache="2">
        <tr align="right" sizset="56" sizcache="2">
            <td sizset="56" sizcache="2"><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0" sizset="56" sizcache="2">
                <tbody sizset="56" sizcache="1">
                    <tr>
                        <td valign="middle"><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.ibm.com/developerworks/cn/java/j-lo-struts2rest/?ca=drs-tp4608#main"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="major5"><span class="atitle">RestActionMapper 简介</span></a></p>
<p>从本质上来看，Struts 2 依然是一个 MVC 框架，最初设计 Struts 2 时并没有按 REST 架构进行设计，因此 Struts 2 本质上并不是一个 REST 框架。由于 Struts 2 提供了良好的可扩展性，因此允许通过 REST 插件将其扩展成支持 REST 的框架。REST 插件的核心是 RestActionMapper，它负责将 Rails 风格的 URL 转换为传统请求的 URL。</p>
<p>用 WinRAR 打开 struts2-rest-plugin-2.1.6 文件，看到该文件里包含一个 struts-plugin.xml 文件，该文件中包含如下一行：</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="57" sizcache="2">
    <tbody sizset="57" sizcache="1">
        <tr>
            <td class="code-outline">
            <pre class="displaycode">&lt;!-- 定义支持 REST 的 ActionMapper --&gt;
            &lt;bean type="org.apache.struts2.dispatcher.mapper.ActionMapper"
            name="rest" class="org.apache.struts2.rest.RestActionMapper" /&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>通过查看 RestActionMapper 的 API 说明，我们发现它可接受如下几个参数：</p>
<ul>
    <li>struts.mapper.idParameterName：用于设置 ID 请求参数的参数名，该属性值默认是 id。
    <li>struts.mapper.indexMethodName：设置不带 id 请求参数的 GET 请求调用 Action 的哪个方法。该属性值默认是 index。
    <li>struts.mapper.getMethodName：设置带 id 请求参数的 GET 请求调用 Action 的哪个方法。该属性值默认是 show。
    <li>struts.mapper.postMethodName：设置不带 id 请求参数的 POST 请求调用 Action 的哪个方法。该属性值默认是 create。
    <li>struts.mapper.putMethodName：设置带 id 请求参数的 PUT 请求调用 Action 的哪个方法。该属性值默认是 update。
    <li>struts.mapper.deleteMethodName：设置带 id 请求参数的 DELETE 请求调用 Action 的哪个方法。该属性值默认是 destroy。
    <li>struts.mapper.editMethodName：设置带 id 请求参数、且指定操作 edit 资源的 GET 请求调用 Action 的哪个方法。该属性值默认是 edit。
    <li>struts.mapper.newMethodName：设置不带 id 请求参数、且指定操作 edit 资源的 GET 请求调用 Action 的哪个方法。该属性值默认是 editNew。 </li>
</ul>
<p>在 RestActionMapper 的方法列表中，我们看到 setIdParameterName、setIndexMethodName、setGetMethodName、setPostMethodName、setPutMethodName、setDeleteMethodName、setEditMethodName、setNewMethodName 等方法，这些方法对应为上面列出的方法提供 setter 支持。</p>
<p>通常情况下，我们没有必要改变 RestActionMapper 的参数，直接使用这些参数的默认值就可支持 Rails 风格的 REST。根据前面介绍可以看出：支持 REST 风格的 Action 至少包含如下 7 个方法：</p>
<ul>
    <li>index：处理不带 id 请求参数的 GET 请求。
    <li>show：处理带 id 请求参数的 GET 请求。
    <li>create：处理不带 id 请求参数的 POST 请求。
    <li>update：处理带 id 请求参数的 PUT 请求。
    <li>destroy：处理带 id 请求参数的 DELETE 请求。
    <li>edit：处理带 id 请求参数，且指定操作 edit 资源的 GET 请求。
    <li>editNew：处理不带 id 请求参数，且指定操作 edit 资源的 GET 请求。 </li>
</ul>
<p>如果请求需要向服务器发送 id 请求参数，直接将请求参数的值附加在 URL 中即可。表 1 显示了 RestActionMapper 对不同 HTTP 请求的处理结果。</p>
<br />
<a name="N10138"><strong>表 1. RestActionMapper 对 HTTP 请求的处理</strong></a><br />
<table class="data-table-1" cellspacing="0" cellpadding="0" border="0" sizset="58" sizcache="2">
    <tbody sizset="58" sizcache="1">
        <tr>
            <td><strong>HTTP 方法</strong> </td>
            <td><strong>URI</strong> </td>
            <td><strong>调用 Action 的方法</strong> </td>
            <td><strong>请求参数</strong> </td>
        </tr>
        <tr>
            <td>GET </td>
            <td>/book </td>
            <td>index </td>
            <td>&nbsp;</td>
        </tr>
        <tr>
            <td>POST </td>
            <td>/book </td>
            <td>create </td>
            <td>&nbsp; </td>
        </tr>
        <tr>
            <td>PUT </td>
            <td>/book/2 </td>
            <td>update </td>
            <td>id=2 </td>
        </tr>
        <tr>
            <td>DELETE </td>
            <td>/book/2 </td>
            <td>destroy </td>
            <td>id=2 </td>
        </tr>
        <tr>
            <td>GET </td>
            <td>/book/2 </td>
            <td>show </td>
            <td>id=2 </td>
        </tr>
        <tr>
            <td>GET </td>
            <td>/book/2/edit </td>
            <td>edit </td>
            <td>id=2 </td>
        </tr>
        <tr>
            <td>GET </td>
            <td>/book/new </td>
            <td>editNew </td>
            <td>&nbsp;</td>
        </tr>
    </tbody>
</table>
<br />
<p>不幸地是，标准 HTML 语言目前根本不支持 PUT 和 DELETE 两个操作，为了弥补这种不足，REST 插件允许开发者提交请求时额外增加一个 _method 请求参数，该参数值可以为 PUT 或 DELETE，用于模拟 HTTP 协议的 PUT 和 DELETE 操作。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="66" sizcache="2">
    <tbody sizset="66" sizcache="1">
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right" sizset="67" sizcache="2">
    <tbody sizset="68" sizcache="2">
        <tr align="right" sizset="68" sizcache="2">
            <td sizset="68" sizcache="2"><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0" sizset="68" sizcache="2">
                <tbody sizset="68" sizcache="1">
                    <tr>
                        <td valign="middle"><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.ibm.com/developerworks/cn/java/j-lo-struts2rest/?ca=drs-tp4608#main"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="major6"><span class="atitle">为 Struts 2 应用安装 REST 插件</span></a></p>
<p>安装 REST 插件非常简单，只需按如下步骤进行即可：</p>
<ol type="1">
    <li>将 Struts 2 项目下 struts2-convention-plugin-2.1.6.jar、struts2-rest-plugin-2.1.6.jar 两个 JAR 包复制到 Web 应用的 WEB-INF\lib 路径下。
    <li>由于 Struts 2 的 REST 插件还需要将提供 XML、JSON 格式的数据，因此还需要将 xstream-1.2.2.jar、json-lib-2.1.jar、ezmorph-1.0.3.jar 以及 Jakarta-Common 相关 JAR 包复制到 Web 应用的 WEB-INF/lib 路径下。
    <li>通过 struts.xml、struts.properties 或 web.xml 改变 struts.convention.default.parent.package 常量的值，让支持 REST 风格的 Action 所在的包默认继承 rest-default，而不是继承默认的 convention-default 父包。 </li>
</ol>
<p>对于第三个步骤而言，开发者完全可以不设置该常量，如果开发者不设置该常量，则意味着开发者必须通过 Annotation 为每个 Action 类设置父包。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="69" sizcache="2">
    <tbody sizset="69" sizcache="1">
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right" sizset="70" sizcache="2">
    <tbody sizset="71" sizcache="2">
        <tr align="right" sizset="71" sizcache="2">
            <td sizset="71" sizcache="2"><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0" sizset="71" sizcache="2">
                <tbody sizset="71" sizcache="1">
                    <tr>
                        <td valign="middle"><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.ibm.com/developerworks/cn/java/j-lo-struts2rest/?ca=drs-tp4608#main"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="major7"><span class="atitle">实现支持 REST 的 Action 类</span></a></p>
<p>在实现支持 REST 的 Action 之前，我们先为系统提供一个 Model 类：Book，该 Book 类非常简单，代码如下：</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="72" sizcache="2">
    <tbody sizset="72" sizcache="1">
        <tr>
            <td class="code-outline">
            <pre class="displaycode">public class Book
            {
            private Integer id;
            private String name;
            private double price;
            // 无参数的构造器
            public Book(){}
            //id 属性的 setter 和 getter 方法
            public void setId(Integer id)
            {
            this.id = id;
            }
            public Integer getId()
            {
            return this.id;
            }
            // 省略 name 和 price 的 setter 和 getter 方法
            ...
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>除了提供上面的 Book 类之外，我们还为该 Book 类提供一个业务逻辑组件：BookService。为了简单起见，BookService 类不再依赖 DAO 组件访问数据库，而是直接操作内存中的 Book 数组——简单地说，本系统中状态是瞬态的，没有持久化保存，应用运行过程中这些状态一直存在，但一旦重启该应用，则系统状态丢失。下面是 BookService 类的代码：</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="73" sizcache="2">
    <tbody sizset="73" sizcache="1">
        <tr>
            <td class="code-outline">
            <pre class="displaycode">public class BookService
            {
            private static Map&lt;Integer , Book&gt; books
            = new HashMap&lt;Integer , Book&gt;();
            // 保留下本图书的 ID
            private static int nextId = 5;
            // 以内存中的数据模拟数据库的持久存储
            static {
            books.put(1 , new Book(1
            , "疯狂 Java 讲义" , 99));
            books.put(2 , new Book(2
            , "轻量级 Java EE 企业应用实战" , 89));
            books.put(3 , new Book(3
            , "疯狂 Ajax 讲义", 78));
            books.put(4 , new Book(4
            , "Struts 2 权威指南" , 79));
            }
            // 根据 ID 获取
            public Book get(int id)
            {
            return books.get(id);
            }
            // 获取系统中全部图书
            public List&lt;Book&gt; getAll()
            {
            return new ArrayList&lt;Book&gt;(books.values());
            }
            // 更新已有的图书或保存新图书
            public void saveOrUpdate(Book book)
            {
            // 如果试图保存的图书的 ID 为 null，表明是保存新的图书
            if (book.getId() == null)
            {
            // 为新的图书分配 ID。
            book.setId(nextId++);
            }
            // 将保存 book
            books.put(book.getId() , book);
            }
            // 删除图书
            public void remove(int id)
            {
            books.remove(id);
            }
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>从上面粗体字代码可以看出，BookService 提供了 4 个方法，用于实现对 Book 对象的 CRUD 操作。</p>
<p>下面开始定义支持 REST 的 Action 类了，这个 Action 类与前面介绍 Struts 2 的普通 Action 存在一些差异——因为该 Action 不再用 execute() 方法来处理用户请求，而是使用前面介绍的 7 个标准方法来处理用户请求。除此之外，该 Action 总是需要处理 id 请求参数，因此必须提供 id 请求参数，并为之提供对应的 setter 和 getter 方法。</p>
<p>因为本系统已经提供了 Book Model 类，并且为了更好的模拟 Rails 中 ActiveController（Controller）直接访问 ActiveRecord（Model）的方式，本系统采用了 ModelDriven 的开发方式，下面是本系统中支持 REST 的 Action 类的代码。</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="74" sizcache="2">
    <tbody sizset="74" sizcache="1">
        <tr>
            <td class="code-outline">
            <pre class="displaycode"> // 定义返回 success 时重定向到 book Action
            @Results(@Result(name="success"
            , type="redirectAction"
            , params = {"actionName" , "book"}))
            public class BookController extends ActionSupport
            implements ModelDriven&lt;Object&gt;
            {
            // 封装 id 请求参数的属性
            private int id;
            private Book model = new Book();
            private List&lt;Book&gt; list;
            // 定义业务逻辑组件
            private BookService bookService = new BookService();
            // 获取 id 请求参数的方法
            public void setId(int id)
            {
            this.id = id;
            // 取得方法时顺带初始化 model 对象
            if (id &gt; 0)
            {
            this.model = bookService.get(id);
            }
            }
            public int getId()
            {
            return this.id;
            }
            // 处理不带 id 参数的 GET 请求
            // 进入首页
            public HttpHeaders index()
            {
            list = bookService.getAll();
            return new DefaultHttpHeaders("index")
            .disableCaching();
            }
            // 处理不带 id 参数的 GET 请求
            // 进入添加新图书。
            public String editNew()
            {
            // 创建一个新图书
            model = new Book();
            return "editNew";
            }
            // 处理不带 id 参数的 POST 请求
            // 保存新图书
            public HttpHeaders create()
            {
            // 保存图书
            bookService.saveOrUpdate(model);
            addActionMessage("添加图书成功");
            return new DefaultHttpHeaders("success")
            .setLocationId(model.getId());
            }
            // 处理带 id 参数的 GET 请求
            // 显示指定图书
            public HttpHeaders show()
            {
            return new DefaultHttpHeaders("show");
            }
            // 处理带 id 参数、且指定操作 edit 资源的 GET 请求
            // 进入编辑页面 (book-edit.jsp)
            public String edit()
            {
            return "edit";
            }
            // 处理带 id 参数的 PUT 请求
            // 修改图书
            public String update()
            {
            bookService.saveOrUpdate(model);
            addActionMessage("图书编辑成功！");
            return "success";
            }
            // 处理带 id 参数，且指定操作 deleteConfirm 资源的方法
            // 进入删除页面 (book-deleteConfirm.jsp)
            public String deleteConfirm()
            {
            return "deleteConfirm";
            }
            // 处理带 id 参数的 DELETE 请求
            // 删除图书
            public String destroy()
            {
            bookService.remove(id);
            addActionMessage("成功删除 ID 为" + id + "的图书！");
            return "success";
            }
            // 实现 ModelDriven 接口必须实现的 getModel 方法
            public Object getModel()
            {
            return (list != null ? list : model);
            }
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>上面 Action 代码中粗体字代码定义了 7 个方法，这 7 个方法正是前面提到的标准方法。除此之外，该 Action 里还包含一个额外的 deleteConfirm() 方法，这个方法用于处理带 id 参数、且指定操作 deleteConfirm 资源的 GET 请求。也就是说，当用户请求 /book/1/deleteConfirm 时，该请求将由该方法负责处理。实际上，RestActionMapper 不仅可以将对 /book/1/edit 的请求映射到 Book 控制器的 edit() 方法，而 1 将作为 id 请求参数。实际上，它可以将任意 /book/1/xxx 的请求映射到 Book 控制器的 xxx() 方法，而 1 是请求参数。上面 Action 类使用了 @Results 进行修饰，这表明当 Action 的任何方法返回&#8220;success&#8221;逻辑视图时，系统将重定向到 book.action。</p>
<p>可能有读者会对 index()、create()、show() 三个方法的返回值感到疑惑：它们不再直接返回普通字符串作为逻辑视图名字，而是返回一个以字符串为参数的 DefaultHttpHeaders 对象？其实读者不必对 DefaultHttpHeaders 感到疑惑，其实 DefaultHttpHeaders 只是普通字符串的加强形式，用于 REST 对处理结果进行更多额外的控制。当 Action 类的处理方法返回字符串作为逻辑视图时，Struts 2 只能将其当成一个简单的视图名，仅能根据该视图名映射到实际视图资源，仅此而已。如果使用 DefaultHttpHeaders 作为逻辑视图，DefaultHttpHeaders 除了可以包含普通字符串作为逻辑视图名之外，还可以额外增加更多的控制数据，从而可以增强对 Response 的控制。关于 HttpHeaders 和 DefaultHttpHeaders 的介绍请参考 REST 插件的 API。</p>
<p>还有一点需要指出，上面的 BookController 控制器实现类的类名并不以 Action 结尾，而是以 Controller 结尾，因此我们可以在 struts.xml 文件中配置如下常量：</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="75" sizcache="2">
    <tbody sizset="75" sizcache="1">
        <tr>
            <td class="code-outline">
            <pre class="displaycode"> &lt;!--  指定控制器类的后缀为 Controller --&gt;
            &lt;constant name="struts.convention.action.suffix"
            value="Controller"/&gt;
            本应用里的 struts.xml 文件如下：
            程序清单：codes\12\12.6\BookShow\WEB-INF\src\struts.xml
            &lt;?xml version="1.0" encoding="GBK" ?&gt;
            &lt;!-- 指定 Struts 2 配置文件的 DTD 信息 --&gt;
            &lt;!DOCTYPE struts PUBLIC
            "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN"
            "http://struts.apache.org/dtds/struts-2.1.dtd"&gt;
            &lt;!-- 指定 Struts 2 配置文件的根元素 --&gt;
            &lt;struts&gt;
            &lt;constant name="struts.i18n.encoding" value="GBK"/&gt;
            &lt;!--  指定控制器类的后缀为 Controller --&gt;
            &lt;constant name="struts.convention.action.suffix"
            value="Controller"/&gt;
            &lt;constant name="struts.convention.action.mapAllMatches"
            value="true"/&gt;
            &lt;!-- 指定 Action 所在包继承的父包 --&gt;
            &lt;constant name="struts.convention.default.parent.package"
            value="rest-default"/&gt;
            &lt;/struts&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="76" sizcache="2">
    <tbody sizset="76" sizcache="1">
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right" sizset="77" sizcache="2">
    <tbody sizset="78" sizcache="2">
        <tr align="right" sizset="78" sizcache="2">
            <td sizset="78" sizcache="2"><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0" sizset="78" sizcache="2">
                <tbody sizset="78" sizcache="1">
                    <tr>
                        <td valign="middle"><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.ibm.com/developerworks/cn/java/j-lo-struts2rest/?ca=drs-tp4608#main"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="major8"><span class="atitle">实现视图层</span></a></p>
<p>定义了上面 Action 之后，接下来应该为这些 Action 提供视图页面了，根据 Convention 插件的约定，所有视图页面都应该放在 WEB-INF\content 目录下，例如当用户向 /book.action 发送请求时，该请求将由 BookController 的 index() 方法进行处理，该方法处理结束后返回&#8220;index&#8221;字符串，也就是将会使用 WEIN-INF\content\book-index.jsp 页面作为视图资源。下面是 book-index.jsp 页面的代码：</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="79" sizcache="2">
    <tbody sizset="79" sizcache="1">
        <tr>
            <td class="code-outline">
            <pre class="displaycode"> &lt;%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %&gt;
            &lt;%@taglib prefix="s" uri="/struts-tags" %&gt;
            &lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
            "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
            &lt;html xmlns="http://www.w3.org/1999/xhtml"&gt;
            &lt;head&gt;
            &lt;title&gt; 图书展示系统 &lt;/title&gt;
            &lt;link href="&lt;%=request.getContextPath() %&gt;/css/demo.css"
            rel="stylesheet" type="text/css" /&gt;
            &lt;/head&gt;
            &lt;body&gt;
            &lt;s:actionmessage /&gt;
            &lt;table&gt;
            &lt;tr&gt;
            &lt;th&gt; 图书 ID&lt;/th&gt;
            &lt;th&gt; 书名 &lt;/th&gt;
            &lt;th&gt; 价格 &lt;/th&gt;
            &lt;th&gt; 操作 &lt;/th&gt;
            &lt;/tr&gt;
            &lt;s:iterator value="model"&gt;
            &lt;tr&gt;
            &lt;td&gt;&lt;s:property value="id"/&gt;&lt;/td&gt;
            &lt;td&gt;${name}&lt;/td&gt;
            &lt;td&gt;${price}&lt;/td&gt;
            &lt;td&gt;&lt;a href="book/${id}"&gt; 查看 &lt;/a&gt; |
            &lt;a href="book/${id}/edit"&gt; 编辑 &lt;/a&gt; |
            &lt;a href="book/${id}/deleteConfirm"&gt; 删除 &lt;/a&gt;&lt;/td&gt;
            &lt;/tr&gt;
            &lt;/s:iterator&gt;
            &lt;/table&gt;
            &lt;a href="&lt;%=request.getContextPath() %&gt;/book/new"&gt; 创建新图书 &lt;/a&gt;
            &lt;/body&gt;
            &lt;/html&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>上面 JSP 页面非常简单，它负责迭代输出 Action 里包含的集合数据，向该应用 book.action 发送请求将看到如图 1 所示页面。</p>
<br />
<a name="fig1"><strong>图 1 使用 Struts 2 开发的 REST 服务</strong></a><br />
<img alt="图 1 使用 Struts 2 开发的 REST 服务" src="http://www.ibm.com/developerworks/cn/java/j-lo-struts2rest/figure001.jpg" /> <br />
<p>Struts 2 的 REST 插件支持一种资源具有多少表示形式，当浏览者向 book.xml 发送请求将可以看到如图 2 所示页面。</p>
<br />
<a name="fig2"><strong>图 2 REST 服务的 XML 形式</strong></a><br />
<img alt="图 2 REST 服务的 XML 形式" src="http://www.ibm.com/developerworks/cn/java/j-lo-struts2rest/figure002.jpg" /> <br />
<p>从图 2 可以看出，该页面正是 Action 所包含的全部数据，当使用 XML 显示时 REST 插件将会负责把这些数据转换成 XML 文档。</p>
<p>除此之外，REST 插件还提供了 JSON 格式的显示方式，当开发者向 book.json 发送请求将看到如图 3 所示页面。</p>
<br />
<a name="fig3"><strong>图 3 REST 服务的 JSON 形式</strong></a><br />
<img alt="图 3 REST 服务的 JSON 形式" src="http://www.ibm.com/developerworks/cn/java/j-lo-struts2rest/figure003.jpg" /> <br />
<p>Struts 2 的 REST 插件默认支持 XHTML、XML 和 JSON 三种形式的数据。</p>
<p>当浏览者单击页面右边的&#8220;编辑&#8221;链接，将会向 book/idVal/edit 发送请求，这是一个包含 ID 请求参数、且指定操作 edit 资源的请求，因此将由 BookController 的 edit() 方法负责处理，处理结束后进入 book-edit.jsp 页面。浏览器里将看到如图 4 所示页面。</p>
<br />
<a name="fig4"><strong>图 4 编辑指定图书</strong></a><br />
<img alt="图 4 编辑指定图书" src="http://www.ibm.com/developerworks/cn/java/j-lo-struts2rest/figure004.jpg" /> <br />
<p>该页面单击&#8220;修改&#8221;按钮时需要修改图书信息，也就是需要使用 PUT 操作，但由于 HTML 不支持 PUT 操作，因此需要为该表单页增加一个额外的请求参数：_method，该请求参数的值为 put。该表单页的代码如下：</p>
<table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="80" sizcache="2">
    <tbody sizset="80" sizcache="1">
        <tr>
            <td class="code-outline">
            <pre class="displaycode"> &lt;%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %&gt;
            &lt;%@taglib prefix="s" uri="/struts-tags" %&gt;
            &lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
            "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
            &lt;html xmlns="http://www.w3.org/1999/xhtml"&gt;
            &lt;head&gt;
            &lt;title&gt; 编辑 ID 为 &lt;s:property value="id"/&gt; 的图书 &lt;/title&gt;
            &lt;link href="&lt;%=request.getContextPath() %&gt;/css/demo.css"
            rel="stylesheet" type="text/css" /&gt;
            &lt;/head&gt;
            &lt;body&gt;
            &lt;s:form method="post"
            action="%{#request.contextPath}/book/%{id}"&gt;
            &lt;!-- 增加 _method 请求参数，参数值为 put 用于模拟 PUT 操作 --&gt;
            &lt;s:hidden name="_method" value="put" /&gt;
            &lt;table&gt;
            &lt;s:textfield name="id" label="图书 ID" disabled="true"/&gt;
            &lt;s:textfield name="name" label="书名"/&gt;
            &lt;s:textfield name="price" label="价格" /&gt;
            &lt;tr&gt;
            &lt;td colspan="2"&gt;
            &lt;s:submit value="修改"/&gt;
            &lt;/td&gt;
            &lt;/table&gt;
            &lt;/s:form&gt;
            &lt;a href="&lt;%=request.getContextPath() %&gt;/book"&gt; 返回首页 &lt;/a&gt;
            &lt;/body&gt;
            &lt;/html&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>该表单将提交给 BookController 的 update() 方法处理，update() 方法将负责修改系统里指定 ID 对应的图书信息。</p>
<p>与之类似的是，当请求需要执行 DELETE 操作时，一样需要增加名为 _method 的请求参数，并将该请求参数值设置为 delete。</p>
<br />
<br />
<p><a name="resources"><span class="atitle">参考资料 </span></a></p>
<ul>
    <li>&#8220;<a href="http://www.ibm.com/developerworks/cn/edu/x-dw-x-restatompp.html">编写 REST 服务</a>&#8221;（developerWorks，2007 年 11 月）：本教程讨论了 REST 和 Atom 发布协议（Atom Publishing Protocol，APP）的概念，并展示如何在服务中进行应用。<br />
    <br />
    <li>&#8220;<a href="http://www.ibm.com/developerworks/cn/edu/j-dw-java-rest-i.html">构建 RESTful Web 服务</a>&#8221;（developerWorks，2008 年 8 月）：本教程从 REST 的基本概念开始，逐步指导您使用 Restlet 框架构建应用程序。<br />
    <br />
    <li>&#8220;<a href="http://www.ibm.com/developerworks/cn/webservices/ws-restful/">基于 REST 的 Web 服务：基础</a>&#8221;（developerWorks，2008 年 12 月）： 在本文中，Alex Rodriguez 将向您介绍 REST 的基本原理。<br />
    <br />
    <li>&#8220;<a href="http://www.ibm.com/developerworks/cn/java/j-lo-struts2-velocity/">进一步提升 Struts 2 对 Velocity 的支持力度</a>&#8221;（developerWorks，2007 年 7 月）：本文主要介绍如何解决 Struts 2 和最新版本的 Velocity 结合的问题以及进一步提升 Struts 2 对 Velocity 的支持力度。<br />
    <br />
    <li>&#8220;<a href="http://www.ibm.com/developerworks/cn/websphere/library/techarticles/0802_yanzhid/">基于 Struts 2 构建 WebSphere Portal 上的 Portlet 应用</a>&#8221;（developerWorks，2008 年 2 月）：本文的目的就是通过在 IBM 的 WebSphere Portal Server 上开发和部署一个基于 Struts 2 的 Porlet 应用，向读者介绍利用 Struts 2 进行 Portlet 应用开发的优势和关键流程。<br />
    <br />
    <li>&#8220;<a href="http://www.ibm.com/developerworks/cn/java/j-lo-struts2-rbac/">基于 Struts 2 拦截器实现细粒度的基于角色的存取控制</a>&#8221;（developerWorks，2008 年 9 月）：本文介绍如何利用 Struts 2 拦截器来为 Java Web 应用添加应用管理的基于角色的存取控制的设计和实现方法。<br />
    <br />
    <li>参考本文作者李刚编著的图书：<a href="http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm">《 Struts 2.1 权威指南——基于 WebWork 核心的 MVC 开发》</a>。 <br />
    <br />
    <li>参考：<a href="http://struts.apache.org/">Struts 2 官方站点</a>。 <br />
    <br />
    <li>阅读技术文章：<a href="http://raibledesigns.com/rd/entry/go_light_with_apache_struts">Go Light with Apache Struts 2 and REST</a>。 <br />
    <br />
    <li><a href="http://www.ibm.com/developerworks/cn/java/">Java 技术专区</a>：寻找 Java 编程各方面的技术文章。</li>
</ul>
<br />
<br />
<p><a name="author"><span class="atitle">关于作者</span></a></p>
<table cellspacing="0" cellpadding="0" width="100%" border="0" sizset="81" sizcache="2">
    <tbody sizset="81" sizcache="1">
        <tr>
            <td colspan="3"><img height="5" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /></td>
        </tr>
        <tr valign="top" align="left">
            <td>
            <p>&nbsp;</p>
            </td>
            <td><img height="5" alt="" src="http://www.ibm.com/i/c.gif" width="4" /></td>
            <td width="100%">
            <p>李刚，从事 Java EE 应用开发近 10 年。曾任 LITEON 公司的 J2EE 技术主管，负责该公司的企业信息化平台的架构设计。曾任广州电信、广东龙泉科技等公司的技术培训教师。疯狂 Java 联盟（http://www.crazyit.org）站长。疯狂 Java 实训营创始人，疯狂 Java 体系图书作者，曾任东方标准广州中心软件教学总监，曾兼任广东技术师范学院计算机科学系的兼职副教授。国内知名IT技术作家，已出版《疯狂 Java 讲义》、《轻量级 Java EE 企业应用实战》、《疯狂 Ajax 讲义》、《Struts 2.1 权威指南》、《Ruby On Rails 敏捷开发最佳实践》等著作。</p>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.blogjava.net/table/aggbug/315146.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/table/" target="_blank">小卓</a> 2010-03-11 11:06 <a href="http://www.blogjava.net/table/articles/315146.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts2访问隐藏的request和session </title><link>http://www.blogjava.net/table/articles/307194.html</link><dc:creator>小卓</dc:creator><author>小卓</author><pubDate>Thu, 24 Dec 2009 10:09:00 GMT</pubDate><guid>http://www.blogjava.net/table/articles/307194.html</guid><wfw:comment>http://www.blogjava.net/table/comments/307194.html</wfw:comment><comments>http://www.blogjava.net/table/articles/307194.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/table/comments/commentRss/307194.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/table/services/trackbacks/307194.html</trackback:ping><description><![CDATA[<div class="postText">
<div style="text-indent: 21.75pt">在Struts1.*中，要想访问request、response以及session等Servlet对象是很方便的，因为它们一直是作为形参在各个方法之间进行传递的，而在Struts2中我们就很难看到它们的芳踪了，因为我们获得表单中的值都是通过预先设置好了的get方法来得到的，那么如果有些参数我们必须通过request.getParametre或者session.getAttribute来得到，那么应该怎么做呢？按照Max的教程上的说法，可以分为两种：IoC方式和非IoC方式，如何理解这两种方式的区别呢？IoC是Spring里面的特征之一，字面意思是反转控制，说白了就是依赖注入，比方说类A依赖类B，那么就主动的给A注入一个类B的对象，下面看一下这两种方法的具体实现。</div>
<div style="text-indent: 21.75pt">1．非Ioc方式</div>
<div style="text-indent: 21.75pt">这种方式主要是利用了<span style="background: red; color: black">com.opensymphony.xwork2.ActionContext</span>类以及<span style="background: red">org.apache.struts2.ServletActionContext</span>类，具体的方法如下所示。</div>
<div style="text-indent: 21.75pt">获得request对象：</div>
<div style="text-indent: 21.75pt"><span style="color: black">A</span><span style="color: black">．</span><span style="color: black">HttpServletRequest request = ServletActionContext.getRequest ();</span></div>
<div style="text-indent: 21.75pt">B．ActionContext ct=<span style="color: black"> ActionContext.<em>getContext</em>()</span></div>
<div style="text-indent: 21.75pt"><span style="color: black">&nbsp;&nbsp; </span><span style="color: black">HttpServletRequest&nbsp;request=</span></div>
<div style="text-indent: 30.7pt"><span style="font-size: 10pt; color: black">(HttpServletRequest)ct.get(ServletActionContext.</span><em><span style="font-size: 10pt; color: #0000c0">HTTP_REQUEST</span></em><span style="font-size: 10pt; color: black">);</span></div>
<div style="text-indent: 21.75pt">获得session对象：</div>
<div style="text-indent: 21.75pt">在Struts2中底层的session都被封装成了Map类型，我们称之为SessionMap，而平常我们所说的session则是指HttpSession对象，具体的获得方法如下所示。</div>
<div style="text-indent: 21.75pt">A．Map session=ActionContext.getSession();</div>
<div style="text-indent: 21.75pt">B．Map session=(Map)ActionContext.getContext().get(ActionContext.SESSION);</div>
<div style="text-indent: 21.75pt">得到这个SessionMap之后我们就可以对session进行读写了，<span style="background: blue; color: white">如果我们想得到原始的</span><span style="background: blue; color: white">HttpSession</span><span style="background: blue; color: white">可以首先得到</span><span style="background: blue; color: white">HttpServletRequest</span><span style="background: blue; color: white">对象，然后通过</span><span style="background: blue; color: white">request.getSession()</span><span style="background: blue; color: white">来取得原始的</span><span style="background: blue; color: white">HttpSession</span><span style="background: blue; color: white">对象</span>。一般情况下SessionMap已经可以完成所有的工作，我们不必再去碰底层的session了。</div>
<div style="text-indent: 21.75pt">2．IoC方式</div>
<div style="text-indent: 21.75pt">这种方式相对来说变化就比较少了，具体流程如下所示。</div>
<div style="text-indent: 21.75pt">获得request对象：</div>
<div style="text-indent: 21.75pt">第一步：让action实现<span style="background: red; color: black">ServletRequestAware</span><span style="color: black">接口</span></div>
<div style="text-indent: 21.75pt"><span style="color: black">第二步：在</span><span style="color: black">action</span><span style="color: black">中声明一个</span><span style="color: black">HttpServletRequest</span><span style="color: black">类型的实例变量</span></div>
<div style="margin-left: 63.75pt; text-indent: -42pt"><span style="color: black">第三步：在</span><span style="color: black">action</span><span style="color: black">中实现</span><span style="background: red; color: black">ServletRequestAware</span><span style="color: black">接口的</span><span style="font-size: 10pt; color: black">setServletRequest</span><span style="font-size: 10pt; color: black">方法，实现方式很简单，如下所示。</span></div>
<div align="left"><span style="font-size: 10pt; color: black">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; color: #7f0055">private</span></strong><span style="font-size: 10pt; color: black"> HttpServletRequest </span><span style="font-size: 10pt; color: #0000c0">request</span><span style="font-size: 10pt; color: black">;</span></div>
<div style="text-indent: 44.25pt" align="left"><strong><span style="font-size: 10pt; color: #7f0055">public</span></strong><strong><span style="font-size: 10pt; color: #7f0055">void</span></strong><span style="font-size: 10pt; color: black"> <span style="background: silver">setServletRequest</span>(HttpServletRequest request) {</span></div>
<div align="left"><span style="font-size: 10pt; color: black">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;</span><strong><span style="font-size: 10pt; color: #7f0055">this</span></strong><span style="font-size: 10pt; color: black">.</span><span style="font-size: 10pt; color: #0000c0">request</span><span style="font-size: 10pt; color: black"> = request;</span></div>
<div style="margin-left: 61.75pt; text-indent: -40pt"><span style="font-size: 10pt; color: black">&nbsp;&nbsp;&nbsp; }</span></div>
<div style="margin-left: 61.75pt; text-indent: -40pt"><span style="font-size: 10pt; color: black">获得</span><span style="font-size: 10pt; color: black">Session</span><span style="font-size: 10pt; color: black">对象</span><span style="font-size: 10pt; color: black">(</span><span style="font-size: 10pt; color: black">注意，此时的</span><span style="font-size: 10pt; color: black">session</span><span style="font-size: 10pt; color: black">是</span><span style="font-size: 10pt; color: black">SessionMap</span><span style="font-size: 10pt; color: black">类型</span><span style="font-size: 10pt; color: black">)</span><span style="font-size: 10pt; color: black">：</span></div>
<div style="text-indent: 21.75pt">第一步：让action实现<span style="font-size: 10pt; background: red; color: black">SessionAware</span><span style="color: black">接口</span></div>
<div style="text-indent: 21.75pt"><span style="color: black">第二步：在</span><span style="color: black">action</span><span style="color: black">中声明一个</span><span style="color: black">HttpServletRequest</span><span style="color: black">类型的实例变量</span></div>
<div style="margin-left: 63.75pt; text-indent: -42pt"><span style="color: black">第三步：在</span><span style="color: black">action</span><span style="color: black">中实现</span><span style="font-size: 10pt; background: red; color: black">SessionAware</span><span style="color: black">接口的</span><span style="font-size: 10pt; background: silver; color: black">setSession</span><span style="font-size: 10pt; color: black">方法，实现方式很简单，如下所示。</span></div>
<div align="left"><span style="font-size: 10pt; color: black">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; color: #7f0055">private</span></strong><span style="font-size: 10pt; color: black"> Map </span><span style="font-size: 10pt; color: #0000c0">session</span><span style="font-size: 10pt; color: black">;</span></div>
<div style="text-indent: 44.25pt" align="left"><strong><span style="font-size: 10pt; color: #7f0055">public</span></strong><strong><span style="font-size: 10pt; color: #7f0055">void</span></strong><span style="font-size: 10pt; color: black"> setSession(Map session) {</span></div>
<div align="left"><span style="font-size: 10pt; color: black">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;</span><strong><span style="font-size: 10pt; color: #7f0055">this</span></strong><span style="font-size: 10pt; color: black">.</span><span style="font-size: 10pt; color: #0000c0"> session</span><span style="font-size: 10pt; color: black"> = session;</span></div>
<div style="margin-left: 61.75pt; text-indent: -40pt"><span style="font-size: 10pt; color: black">&nbsp;&nbsp;&nbsp; }</span></div>
<div style="margin-left: 63.75pt; text-indent: -42pt">&nbsp;Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1721226</div>
</div>
<img src ="http://www.blogjava.net/table/aggbug/307194.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/table/" target="_blank">小卓</a> 2009-12-24 18:09 <a href="http://www.blogjava.net/table/articles/307194.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>对struts2的底层源码的解读</title><link>http://www.blogjava.net/table/articles/307164.html</link><dc:creator>小卓</dc:creator><author>小卓</author><pubDate>Thu, 24 Dec 2009 07:42:00 GMT</pubDate><guid>http://www.blogjava.net/table/articles/307164.html</guid><wfw:comment>http://www.blogjava.net/table/comments/307164.html</wfw:comment><comments>http://www.blogjava.net/table/articles/307164.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/table/comments/commentRss/307164.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/table/services/trackbacks/307164.html</trackback:ping><description><![CDATA[<div>
<p style="margin: 0cm 0cm 0pt"><span style="font-size: 12pt; color: blue">此问题网上很多人在讨论，至今还是没有很好的解决方案，在我对源码进行解读后，现在已经有个很好的解决方案，业务是用户注册（含复选框），用户注册验证出错后，错误信息的要显示在相应控件的后面，同时要让请求选中的复选框处于选中状态。希望您往下看，说不定会小有收获哦！！</span></p>
<p style="margin: 0cm 0cm 0pt">步骤：</p>
<p style="margin: 0cm 0cm 0pt; text-align: left" align="left"><span style="font-size: 10pt">1)</span><span style="font-size: 10pt">开发工具设置</span></p>
<p style="margin: 0cm 0cm 0pt; text-align: left" align="left"><span style="font-size: 10pt">&nbsp;&nbsp; a)</span><span style="font-size: 10pt">工程编码</span><span style="font-size: 10pt">utf-8</span></p>
<p style="margin: 0cm 0cm 0pt; text-align: left" align="left"><span style="font-size: 10pt">&nbsp;&nbsp; b)</span><span style="font-size: 10pt">文件代码样式</span><span style="font-size: 10pt">（</span><span style="font-size: 10pt">java</span><span style="font-size: 10pt">、</span><span style="font-size: 10pt">xml</span><span style="font-size: 10pt">）</span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-size: 10pt">&nbsp;&nbsp; c)</span><span style="font-size: 10pt">文件编码格式</span><span style="font-size: 10pt">utf-8</span><span style="font-size: 10pt">（</span><span style="font-size: 10pt">jsp</span><span style="font-size: 10pt">、</span><span style="font-size: 10pt">html</span><span style="font-size: 10pt">、</span><span style="font-size: 10pt">js</span><span style="font-size: 10pt">）</span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-size: 10pt">2)</span><span style="font-size: 10pt">创建目录结构</span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-size: 10pt"><img src="http://hiphotos.baidu.com/lbchjf/pic/item/f7c08b7bf1b1f5c22f73b36b.jpg" border="0" small="0"  alt="" /></span></p>
<span style="font-size: 10pt">
<p style="margin: 0cm 0cm 0pt"><font size="3"><font face="Times New Roman">3) </font>搭建<font face="Times New Roman">struts2</font>的环境，</font></p>
<p style="margin: 0cm 0cm 0pt 60pt; text-indent: -18pt"><span style="font-size: 10pt">a)<span style="font: 7pt Times New Roman">&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 10pt">导入</span><span style="font-size: 10pt">jar</span><span style="font-size: 10pt">包</span></p>
<p style="margin: 0cm 0cm 0pt 42pt; text-indent: 21pt"><font size="3">参考<font face="Times New Roman">struts2.1.8</font>下的<font face="Times New Roman">app</font>下的<font face="Times New Roman">struts2-blank-2.1.8</font>项目，拷入基本的<font face="Times New Roman">jar</font>包</font></p>
<p style="margin: 0cm 0cm 0pt"><font face="Times New Roman" size="3">==========================================================</font></p>
<p style="margin: 0cm 0cm 0pt 21pt; text-indent: 21pt; text-align: left" align="left"><span style="font-size: 10pt; color: black">struts2-core-2.x.x.jar </span><span style="font-size: 10pt; color: blue">:Struts 2</span><span style="font-size: 10pt; color: blue">框架的核心类库</span></p>
<p style="margin: 0cm 0cm 0pt; text-align: left" align="left"><span style="font-size: 10pt; color: blue">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-size: 10pt; color: black">xwork-2.x.x.jar</span><span style="font-size: 10pt; color: blue"> :XWork</span><span style="font-size: 10pt; color: blue">类库，</span><span style="font-size: 10pt; color: blue">Struts 2</span><span style="font-size: 10pt; color: blue">在其上构建</span></p>
<p style="margin: 0cm 0cm 0pt 42pt; text-align: left" align="left"><span style="font-size: 10pt; color: black">ognl-2.6.x.jar </span><span style="font-size: 10pt; color: blue">:</span><span style="font-size: 10pt; color: blue">对象图导航语言（</span><span style="font-size: 10pt; color: blue">Object Graph Navigation Language</span><span style="font-size: 10pt; color: blue">），</span><span style="font-size: 10pt; color: blue">struts2</span><span style="font-size: 10pt; color: blue">框架通过其读写对象的属性</span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-size: 10pt; color: blue">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-size: 10pt; color: black">freemarker-2.3.x.jar</span><span style="font-size: 10pt; color: blue"> :Struts 2</span><span style="font-size: 10pt; color: blue">的</span><span style="font-size: 10pt; color: blue">UI</span><span style="font-size: 10pt; color: blue">标签的模板使用</span><span style="font-size: 10pt; color: blue">FreeMarker</span><span style="font-size: 10pt; color: blue">编写</span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-size: 10pt; color: blue">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-size: 10pt; color: black">commons-fileupload-1.2.1.jar</span><span style="font-size: 10pt; color: black">、</span><span style="font-size: 10pt; color: black">commons-io-1.3.2.jar</span><span style="font-size: 10pt; color: blue">这两个</span><span style="font-size: 10pt; color: blue">jar</span><span style="font-size: 10pt; color: blue">是用于文件上传</span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-size: 10pt; color: black">===========================================================</span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; b)</span><span style="font-size: 10pt">修改</span><span style="font-size: 10pt">web.xml</span><span style="font-size: 10pt">配置文件、添加</span><span style="font-size: 10pt">struts.xml</span><span style="font-size: 10pt">文件</span></p>
<p style="margin: 0cm 0cm 0pt"><span style="font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c)</span><span style="font-size: 10pt">启动项目测试</span><span style="font-size: 10pt">index.jsp</span><span style="font-size: 10pt">界面输出</span></p>
<p style="margin: 0cm 0cm 0pt"><font size="3"><font face="Times New Roman">3</font>）注册页面</font></p>
<p style="margin: 0cm 0cm 0pt">页面的源码如下：粗心了点没有实现纯国际化</p>
<p style="margin: 0cm 0cm 0pt"><img src="http://hiphotos.baidu.com/lbchjf/pic/item/86ee12c26f3ab57ab219a871.jpg" border="0" small="0"  alt="" /><br />
</p>
<p style="margin: 0cm 0cm 0pt">相信您已经推断出我的项目大致部署，我这里就简要描述下：</p>
<p style="margin: 0cm 0cm 0pt">配置文件：<span style="color: blue"><font face="Times New Roman">struts.xml</font></span><span style="color: blue">和</span><span style="color: blue"><font face="Times New Roman">strus-user.xml</font></span><span style="color: blue">基于模块化配置，</span><span style="color: blue"><font face="Times New Roman">UserAction-registerUser-validation.xml</font></span><span style="color: blue">在执行</span><span style="color: blue"><font face="Times New Roman">UserAction</font></span><span style="color: blue">的</span><span style="color: blue"><font face="Times New Roman">registerUser</font></span><span style="color: blue">方法时进行校验</span></p>
<p style="margin: 0cm 0cm 0pt">属性文件：<span style="color: blue">有全局的国际化文件和</span><span style="color: blue"><font face="Times New Roman">user</font></span><span style="color: blue">模块的国际化文件</span></p>
<p style="margin: 0cm 0cm 0pt">类：<span style="color: blue"><font face="Times New Roman">UserAction</font></span><span style="color: blue">、</span><span style="color: blue"><font face="Times New Roman">UserBean</font></span><span style="color: blue">、</span><span style="color: blue"><font face="Times New Roman">Hobby</font></span></p>
<span><span>
<p style="margin: 0cm 0cm 0pt">工程目录</p>
<p style="margin: 0cm 0cm 0pt"><img src="http://hiphotos.baidu.com/lbchjf/pic/item/c7d887f24089567f342acc7c.jpg" border="0" small="0"  alt="" /><br />
</p>
<p style="margin: 0cm 0cm 0pt"><font face="Times New Roman">Struts.xml</font>文件</p>
<p style="margin: 0cm 0cm 0pt"><img src="http://hiphotos.baidu.com/lbchjf/pic/item/9888882bbb4caf175243c17e.jpg" border="0" small="0"  alt="" /><br />
</p>
<p style="margin: 0cm 0cm 0pt"><font face="Times New Roman">UserAction</font>类</p>
<p style="margin: 0cm 0cm 0pt"><span style="color: red"><img src="http://hiphotos.baidu.com/lbchjf/pic/item/a5e73a954ad0c231d31b7078.jpg" border="0" small="0"  alt="" /><br />
</span></p>
</span></span></span><img src="http://hiphotos.baidu.com/lbchjf/pic/item/a1ceb42557102804d5074276.jpg" border="0" small="0"  alt="" /><br />
<br />
<div>工程目录</div>
<div>Struts.xml文件</div>
<div>UserAction类</div>
<div><span style="color: red">第一个问题出现了，如下图：</span></div>
<div><span style="color: blue">说明</span>：</div>
<div>很多人都是对此很烦恼，struts2使用的默认主题是xhtml，查看html页面的源码发现，它给我们生成了表格布局，所以界面比较整齐，但在提供便利的同时，也带来些烦恼，就是错误提示出现的位置。</div>
<div><span style="color: blue">解决方案</span>：</div>
<div>1）把主题设为theme=simple，自己去控制布局，</div>
<div>2）struts使用freemarker模版技术，为我们标签生成了html代码，所以我们通过修改模版设置错误信息提示的位置。</div>
<div>解读源码了：</div>
<div>&nbsp;</div>
<div>a: &lt;s:textfield/&gt;这是strus2给我们提供标签，所以我们查看官方文档得知struts-tags.tld在struts-core.jar文件里，每个标签都是一个java类，只是该类extends TagSupport，我们查看TextFieldTag类，<span style="color: blue">其实有经验的人都能猜的八九不离十，您肯定是的。</span></div>
<div>查看TextFieldTag类</div>
<div>发现它没有doStartTag()方法，所以猜肯定在父类里定义了，查看AbstractUITag，这个类里也没有doStartTag()方法，这个类是所有UI标签的父类，里面定义了标签的属性</div>
<div>继续查看父类，</div>
<div>该类有doStartTag()方法，</div>
<div>我们查看得知，container容器注入了component组件，组件会话出html文本，所有我们需要查看着个组件的具体实现类，在TextFieldTag里面发现</div>
<div>查看Component具体类TextField，</div>
<div>查看注释得知该类构建html文本，但为什么TextField没有继承Component类呢？一猜就是UIBean继承了，查看</div>
<div>果然是继承了Component，学框架思想最重要的是看别人的注释，因为注释是别人思想的表达，这个类负责通过模版构建html文本，所以我们最重要的是找到模版的位置，<span style="color: blue">这个我也是大概看懂，毕竟还没达到水平，相信您已经达到，大致意思是找这些属性值，从而定位到</span>ftl模版文件</div>
<divre style="background: #f0f0f0"></divre>
<span style="color: black">struts.ui.theme=xhtml</span>
<pre>&nbsp;</pre>
<pre style="background: #f0f0f0"><span style="color: black">struts.ui.templateDir=template</span></pre>
<pre style="background: #f0f0f0"><span style="color: black">struts.ui.templateSuffix=ftl</span></pre>
<p>如:&lt;s:checkbox/&gt; 找/template/xhtml/checkbox.ftl模版文件，类推&lt;s:textfield/&gt;,在strus2-core.jar下找textfield.ftl，发现是text.ftl,打开我们查看：</p>
<div>&nbsp;</div>
<div>包含三个模版，controlheader.ftl又包含<span style="font-size: 10pt">controlheader-core.ftl</span><span style="font-size: 10pt">文件，该文件才是核心，</span></div>
<div>现在我们要修改controlheader-core.ftl、controlfooter.ftl文件，把错误信息放到controlfooter.ftl里面，我们可以有两种方式修改：</div>
<div style="margin: 0cm 0cm 0pt 18pt; text-indent: -18pt"><span>1）&nbsp;</span>拷贝出两个文件，修改后再压缩进去（严重不建议！）</div>
<div style="margin: 0cm 0cm 0pt 18pt; text-indent: -18pt"><span>2）&nbsp;</span>既然有这样的需求，strus2团队肯定考虑到了，这让我实在太佩服他们了，每个细节考虑的都是那么周到，看官方文档</div>
<div>Strus2团队提出了模版的加载、选择模版目录、选择主题、扩展主题。我们想让框架加载我们的模版文件，所以我们点击Template Loading链接查看，</div>
<div>&nbsp;</div>
<div>意思大概是，首先加载应用程序路径下模版文件，然后去加载classpath下的模版文件，如果需要覆盖某模版，拷贝修改后放置应用程序下，那样将首先加载。本人英语不是太好，如有出处，请见谅！</div>
<div>现在我们需要把两个ftl文件放置application下，有什么格式要求吗？</div>
<div>本章里strus2团队还给我们提供很多建议，建议我们不要随便更换模版引擎，如要修改ftl文件最好把源文件拷贝出来加以修改，不要自己手工重写，以防止出错。</div>
<div>相信也不用解释了，格式为：<tt><span style="font-size: 12pt; color: black">/template/$theme/$template.ftl</span></tt><span style="font-size: 9pt; color: black">.</span></div>
<div>所以我们只要在Web-Root下创建/template/xhtml文件夹，拷贝controlheader-core.ftl、controlfooter.ftl文件再加以修改。</div>
<div>本人对于freemarker语言不是很了解，但掌握了思想，这种增增删删的操作还是可以应付的，经过几轮修改，总于搞定了。</div>
<div>----------------------------------------------------------------------------------------------------</div>
<div><span style="color: red">第二个问题：</span></div>
<div>先对strus2的默认拦截器原理说下</div>
<div>官方文档这两行最能表达我的意思，excludeMethods参数是设置该拦截器忽略哪些方法，下面反之。<span style="color: blue">这简单原理相信您非常了解了，来看下我们这里出现的情况吧！</span></div>
<div>这是最普遍的业务逻辑，</div>
<div>这是strus-user.xml配置文件里的registerUser*.action配置：</div>
<div><span style="color: red">情况一：</span></div>
<div>我们按此配置运行，我们不填用户名注册，结果出现如下情况：</div>
<div>说明：</div>
<div>为什么出现无法显示网页呢？我们注册首先被validate拦截器拦截后，UserAction-registerUser-validation.xml验证用户名不能为空，经过workflow拦截器，发现验证出错了，所以跳转input试图，我们input没有配置type，默认是dispatcher，我们这样转发的话，则不再被strus2的拦截器拦截，相当于浏览器访问registerUserUI静态资源，因为压根没有，<span style="color: blue">所有</span><span style="color: blue">tomcat</span><span style="color: blue">容器无法访问，则出现无法显示网页</span>。</div>
<div><span style="color: red">情况二：</span></div>
<div>好的我们现在修改input结果的type属性为redirect，</div>
<div>我们选中两个爱好，提交返回界面如下：</div>
<div><span style="color: blue">开始选中的被取消了，并且没有&#8220;用户名不能为空&#8221;的错误信息。</span></div>
<div>说明：</div>
<div>为什么会错误信息没了呢？同样经过validate、workflow拦截器后，跳转input试图，一但我们重定向，则<span style="color: blue">strus2</span><span style="color: blue">框架会为我们创建一个新的</span><span style="color: blue">UserAction</span><span style="color: blue">对象</span>，则fieldErrors、<span style="font-size: 10pt; color: black">UserBean</span><span style="font-size: 10pt; color: black">的</span><span style="font-size: 10pt; color: black">hobby</span><span style="font-size: 10pt; color: black">数组都为</span><span style="font-size: 10pt; color: black">null</span><span style="font-size: 10pt; color: black">了，所以错误信息、开始选中的都显示不出来了。</span></div>
<div><span style="font-size: 10pt; color: red">情况三：</span></div>
<div><span style="font-size: 10pt; color: red">相信您已经有解决方案了</span><span style="font-size: 10pt; color: black">，把</span><span style="font-size: 10pt; color: black">input</span><span style="font-size: 10pt; color: black">试图的</span><span style="font-size: 10pt; color: black">type</span><span style="font-size: 10pt; color: black">设为</span><span style="font-size: 10pt; color: black">chain</span><span style="font-size: 10pt; color: black">类型，</span><span style="font-size: 10pt; color: black">chain</span><span style="font-size: 10pt; color: black">类型的作用是，让该请求重新被拦截器拦截，好我们来修改：</span></div>
<div>我们同样不填用户名，选中两个爱好，点击注册后，发现如下结果：</div>
<div>意思大概是：<span style="color: blue">发现一个无限递归调用。</span></div>
<div>说明：</div>
<div>怎么会出现这样无限递归呢？哪里在递归调用？我们来分析一下流程，当经过validate、workflow拦截器后，跳转input试图，此时type=chain，所以我们看下设置为chain类型后，struts2到底都干了些什么？在官方文档<strong><span style="font-size: 18.5pt; color: #003366">Action Chaining</span></strong>一章里讲解到，</div>
<div><span style="color: red">大概意思是</span><span style="color: #ff6600">：</span>如果你要拷贝当前的action属性到当前chain上的action，你需要应用Chaining拦截器，<span style="color: blue">该拦截器会拷贝请求上的</span><span style="color: blue">parameters</span><span style="color: blue">和</span><span style="color: blue">value stack</span><span style="color: blue">传递到目标</span><span style="color: blue">action</span><span style="color: blue">上</span>，原始的action会保存valueStack，允许目标action访问前面所有action的valueStack作用域上的属性，同样对于chain的result结果试图如jsp、velocity界面同样可以访问这些属性。</div>
<div>=====================================================</div>
<div>所以当我们以chain方式访问registerUserUI.action，原先action的valueStack上的fieldErrors同样被拷贝到当前UserAction对象fieldErrors字段里，在经过workflow拦截器后，又发现有错误，同样调用input试图，进而又去调用registerUserUI.action，经过workflow拦截器后，又发现有错误，又去调用input试图，进而递归调用。</div>
<div>现在该怎样解决呢？我们要做的就是，让他调用registerUserUI.action时被workflow拦截器拦截后，不再验证是否有错误，前面说到过excludeMethods参数，我们查看workflow拦截器</div>
<div>发现他忽略input方法，所以我们加上这样的配置：</div>
<div>修改UserAction的registerUserUI方法为input，ok了！我们运行同样不填用户名、选中两个爱好提交，运行结果为：</div>
<div>注意：</div>
<div>虽然chain方式对于完成这种业务很方便，官方提示我们谨慎使用，过度使用会造成程序的代码混乱，到时还是根据业务来决定。</div>
<br />
</div>
<img src ="http://www.blogjava.net/table/aggbug/307164.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/table/" target="_blank">小卓</a> 2009-12-24 15:42 <a href="http://www.blogjava.net/table/articles/307164.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>