<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>andlu</title>
    <description></description>
    <link>http://andlu.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>Struts2中的零配置与CoC（Convention over Configration）</title>
        <author>andlu</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://andlu.javaeye.com">andlu</a>&nbsp;
          链接：<a href="http://andlu.javaeye.com/blog/112675" style="color:red;">http://andlu.javaeye.com/blog/112675</a>&nbsp;
          发表时间: 2007年08月15日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          摘要：介绍Struts2中的零配置（Zero Configuration），以及如何用COC来更好地简化Struts2的配置。在第一章，我使用Maven来创建一个起点项目；第二章，以该项目为例，讲解如何使用Struts2的零配置；第三章，论述第二章中的实现方式的缺陷，然后讲解如何使用COC来改进这些缺陷，并进一步简化Struts2的配置。附件是这篇文章用到的示例代码。<br />
<br />
<h2>一、从零开始</h2>
这里，我将建立一个新的示例项目，作为讲解的起点。我使用JDK 6、Maven 2、Eclipse 3.3来建立这个示例，如果读者对Maven2不熟也没关系，这只是个示例。<br />
首先，运行下边的命令：<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; mvn archetype:create -DgroupId=demo.struts -DartifactId=demo-struts-coc -DarchetypeArtifactId=maven-archetype-webapp<br />
这会建立如下的目录结构：<demo-struts-coc><br />
&nbsp;|- POM.xml<br />
&nbsp;|- src<br />
&nbsp;&nbsp; &nbsp; |- main<br />
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; |- resources<br />
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; |- webapp<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp; |- index.jsp<br />
&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |- WEB-INF<br />
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |- web.xml<br />
然后我们在src/main目录下新建一个名为java的目录，用来放置java代码。在src下建立test目录，并在test目录下建立java目录，用来放置测试代码。另外，我这个示例不想使用JSP，所以我将src/main/webapp目录下的index.jsp改为index.html。<br />
现在，需要配置该项目要用到哪些lib。在POM.xml中加入struts2-core：<br />
</demo-struts-coc>
<div class="code_title">xml 代码</div>
<div class="dp-highlighter">
<div class="bar">&nbsp;</div>
<ol class="dp-xml" start="1">
    <li class="alt"><span><span class="tag">&lt;</span><span class="tag-name">dependency</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="tag">&lt;</span><span class="tag-name">groupId</span><span class="tag">&gt;</span><span>org.apache.struts</span><span class="tag">&lt;/</span><span class="tag-name">groupId</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="tag">&lt;</span><span class="tag-name">artifactId</span><span class="tag">&gt;</span><span>struts2-core</span><span class="tag">&lt;/</span><span class="tag-name">artifactId</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="tag">&lt;</span><span class="tag-name">version</span><span class="tag">&gt;</span><span>2.0.9</span><span class="tag">&lt;/</span><span class="tag-name">version</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span><span class="tag">&lt;/</span><span class="tag-name">dependency</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
</ol>
</div>
<br />
另外，我想在Eclipse里使用jetty来启动项目并进行测试，所以在POM.xml中再加入jetty、jetty-util、servlet-api等的依赖，详情见附件。<br />
我希望使用Eclipse来作为这个项目的IDE，所以，我在命令行状态下，进入这个项目所在的目录，运行：<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; mvn eclipse:eclipse<br />
然后使用Eclipse导入这个项目。如果你是第一次用Eclipse导入用Maven生成的项目，那你需要在Eclipse里配置一个名叫M2_REPO的Variable，指向你的Maven 2的repository目录。缺省情况下，它应该位于${user.home}/.m2/repository。<br />
OK！现在我们已经可以在Eclipse中进行工作了。<br />
修改src/main/webapp/WEB-INF/web.xml，加入struts2的FilterDispatcher并设置filter-mapping。在这个示例中我将url-pattern设为&quot;/app/*&quot;，也就是说，url的匹配是基于路径来做的。这只是我的个人喜好而已，你也可以将它设成&quot;*&quot;。<br />
既然是在讲struts2的零配置，当然是可以不要任何配置文件的。但是为了更好地进行&ldquo;配置&rdquo;，我还是建立了struts.xml文件（在src/main/resources目录下）。我不喜欢url最后都有个action后缀，现在，我在struts.xml中配置struts.action.extension，将这个后缀去掉：<br />
<div class="code_title">xml 代码</div>
<div class="dp-highlighter">
<div class="bar">&nbsp;</div>
<ol class="dp-xml" start="1">
    <li class="alt"><span><span class="tag">&lt;</span><span class="tag-name">struts</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="tag">&lt;</span><span class="tag-name">constant</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;struts.action.extension&quot;</span><span>&nbsp;</span><span class="attribute">value</span><span>=</span><span class="attribute-value">&quot;&quot;</span><span>&nbsp;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span><span class="tag">&lt;/</span><span class="tag-name">struts</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
</ol>
</div>
<br />
然后我在src/test/java下建立demo/RunJetty.java文件，main方法如下：<br />
<div class="code_title">java 代码</div>
<div class="dp-highlighter">
<div class="bar">&nbsp;</div>
<ol class="dp-j" start="1">
    <li class="alt"><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;main(String[]&nbsp;args)&nbsp;</span><span class="keyword">throws</span><span>&nbsp;Exception&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;Server&nbsp;server&nbsp;=&nbsp;<span class="keyword">new</span><span>&nbsp;Server(</span><span class="number">8080</span><span>);&nbsp;</span><span class="comment">//也可以改成其它端口</span><span>&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;File&nbsp;rootDir&nbsp;=&nbsp;<span class="keyword">new</span><span>&nbsp;File(RunJetty.</span><span class="keyword">class</span><span>.getResource(</span><span class="string">&quot;/&quot;</span><span>).getPath()).getParentFile().getParentFile();&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;webAppPath&nbsp;=&nbsp;<span class="keyword">new</span><span>&nbsp;File(rootDir,&nbsp;</span><span class="string">&quot;src/main/webapp&quot;</span><span>).getPath();&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">new</span><span>&nbsp;WebAppContext(server,&nbsp;webAppPath,&nbsp;</span><span class="string">&quot;/&quot;</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;server.start();&nbsp;&nbsp;</span></li>
    <li class="alt"><span>}&nbsp;&nbsp;</span></li>
</ol>
</div>
<br />
现在，在Eclipse里运行或调试这个RunJetty.java，用浏览器打开http://localhost:8080/看看吧。如果不出问题，应该可以访问到webapp目录下的index.html了。有了Jetty，你还在用MyEclipse或其它插件么？<br />
<br />
<h2>二、零配置</h2>
首先要澄清一点，这里说的零配置并不是一点配置都没有，只是说配置很少而已。<br />
Struts2（我只用过Struts 2.0.6和2.0.9，不清楚其它版本是否支持零配置）引入了零配置的新特性，元数据可以通过规则和注解来表达：A &quot;Zero Configuration&quot; Struts application or plugin uses no additional XML or properties files. Metadata is expressed through convention and annotation.<br />
目前，这个新特性还在测试阶段，但经过一段时间的使用，我觉得这个特性已经可用。下面我讲一下如何使用它。<br />
1. Actions的定位<br />
以前需要在xml配置文件中配置Action的name和class，如果使用零配置，所带来的一个问题就是如何定位这些Action。我们需要在web.xml中找到struts2的filter的配置，增加一个名为actionPackages的init-param，它的值是一个以逗号分隔的Java包名列表，比如：demo.actions1,demo.actions2。struts2将会扫描这些包（包括这些包下边的子包），在这些包下，所有实现了Action接口的或者是类名以&ldquo;Action&rdquo;结尾的类都会被检查到，并被当做Action。<br />
以前，我们写Action必须要实现Action接口或者继承ActionSupport。但是，上面提到的类名以&quot;Action&quot;结尾的类并不需要这样做，它可以是一个POJO，Struts2支持POJO Action！<br />
下面是actionPackages的配置示例：<br />
<div class="code_title">xml 代码</div>
<div class="dp-highlighter">
<div class="bar">&nbsp;</div>
<ol class="dp-xml" start="1">
    <li class="alt"><span><span class="tag">&lt;</span><span class="tag-name">filter</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;<span class="tag">&lt;</span><span class="tag-name">filter-name</span><span class="tag">&gt;</span><span>struts2</span><span class="tag">&lt;/</span><span class="tag-name">filter-name</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;<span class="tag">&lt;</span><span class="tag-name">filter-class</span><span class="tag">&gt;</span><span>org.apache.struts2.dispatcher.FilterDispatcher</span><span class="tag">&lt;/</span><span class="tag-name">filter-class</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;<span class="tag">&lt;</span><span class="tag-name">init-param</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="tag">&lt;</span><span class="tag-name">param-name</span><span class="tag">&gt;</span><span>actionPackages</span><span class="tag">&lt;/</span><span class="tag-name">param-name</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="tag">&lt;</span><span class="tag-name">param-value</span><span class="tag">&gt;</span><span>demo.actions1,demo.actions2</span><span class="tag">&lt;/</span><span class="tag-name">param-value</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;<span class="tag">&lt;/</span><span class="tag-name">init-param</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span><span class="tag">&lt;/</span><span class="tag-name">filter</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
</ol>
</div>
<br />
2. 示例<br />
现在我们建立demo.actions1.app.person和demo.actions2.app.group两个包，在demo.actions1.app.person包下建立ListPeopleAction.java，在demo.actions2.app.group下建立ListGroupAction.java。作为示例，这两个类只是包含一个execute方法，返回&quot;success&quot;或&quot;error&quot;，其它什么都不做：<br />
<div class="code_title">java 代码</div>
<div class="dp-highlighter">
<div class="bar">&nbsp;</div>
<ol class="dp-j" start="1">
    <li class="alt"><span><span class="keyword">public</span><span>&nbsp;String&nbsp;execute()&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;</span><span class="string">&quot;success&quot;</span><span>;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>}&nbsp;&nbsp;</span></li>
</ol>
</div>
<br />
在Filter的配置中，我指定actionPackages为demo.actions1,demo.actions2，当系统启动时，Struts2就会在这两个包下扫描到demo.actions1.app.person.ListPeopleAction和demo.actions2.app.group.ListGroupAction。<br />
<br />
3. Action and Package name<br />
Struts2扫描到Action后，从actionPackages指定的包开始，子包名会成为这个Action的namespace，而Action的name则由这个Action的类名决定。将类名首字母小写，如果类名以Action结尾，则去掉&quot;Action&quot;后缀，形成的名字就是这个Action的名字。在如上所述的示例中，actionPackages指定为demo.actions1,demo.actions2，那么你可以这样访问demo.actions1.app.person.ListPeopleAction：<br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; http://localhost:8080/app/person/listPeople<br />
<br />
4. Results<br />
Struts2是通过&quot;Result&quot;和&quot;Results&quot;两个类级别的annotations来指定Results的。<br />
作为示例，我们在webapp目录下建两个html文件：success.html和error.html，随便写点什么内容都可以。现在假设我们访问/app/person/listPeople时，或Action返回success就转到success.html页面，若是error就转到error.html页面，这只需要在ListPeopleAction类上加上一段注解就可以了：<br />
<div class="code_title">java 代码</div>
<div class="dp-highlighter">
<div class="bar">&nbsp;</div>
<ol class="dp-j" start="1">
    <li class="alt"><span><span class="annotation">@Results</span><span>({&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="annotation">@Result</span><span>(name=</span><span class="string">&quot;success&quot;</span><span>,&nbsp;type=NullResult.</span><span class="keyword">class</span><span>,&nbsp;value&nbsp;=&nbsp;</span><span class="string">&quot;/success.html&quot;</span><span>,&nbsp;params&nbsp;=&nbsp;{}),&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="annotation">@Result</span><span>(name=</span><span class="string">&quot;error&quot;</span><span>,&nbsp;type=NullResult.</span><span class="keyword">class</span><span>,&nbsp;value&nbsp;=&nbsp;</span><span class="string">&quot;/error.html&quot;</span><span>,&nbsp;params&nbsp;=&nbsp;{})&nbsp;&nbsp;</span></span></li>
    <li class=""><span>})&nbsp;&nbsp;</span></li>
    <li class="alt"><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;ListPeopleAction&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;String&nbsp;execute()&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;</span><span class="string">&quot;success&quot;</span><span>;&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class="alt"><span>}&nbsp;&nbsp;</span></li>
</ol>
</div>
<br />
同上，我们给ListGroupAction也加上注解。<br />
现在，我们已经完成了一个零配置的示例。我们并没有在xml文件里配置ListPeopleAction和ListGroupAction，但它们已经可以工作了！<br />
用Eclipse运行RunJetty，然后用浏览器访问http://localhost:8080/app/person/listPeople和http://localhost:8080/app/group/listGroup看看，是不是正是success.html（或error.html）的内容？<br />
<br />
5. Namespaces<br />
如上所述，namespace由包名所形成，但我们可以使用&quot;Namespace&quot;注解来自己指定namespace。<br />
<br />
6. Parent Package<br />
这个配置用得较少。Struts2提供一个&quot;ParentPackage&quot;注解来标识Action应该是属于哪个package。<br />
<br />
<h2>三、使用COC</h2>
如上所述，Struts2用注解来实现零配置。然而，这不是我喜欢的方式。在我看来，这不过是将配置从XML格式换成了注解方式，并不是真的零配置。而且，这种方式也未必比XML形式的配置更好。另外，对元数据的修改必然会导致项目的重新编译和部署。还有，现在的Struts2版本似乎对Result注解中的params的处理有些问题。<br />
其实，Struts2的actionPackages配置已经使用了COC，那为什么不能为Results也实现COC，从而去除这些每个Action都要写的注解？<br />
在严谨的项目中，package、action的名称和页面的路径、名称一定存在着某种关系。比如，页面的路径可能和package是对应的，页面的名称可能和action的名称是对应的，或是根据某种法则运算得到。我们知道webwork2和struts2有个配置叫global-results。我们为什么不能根据这些对应规则写个Result，将它配到global-results中，从而真正免去result的配置？<br />
事实上，我推荐Struts2的使用者只用Struts2输出XML或JSON，放弃UI，页面这层还是使用标准的HTML、CSS和一些JS组件来展现。许多人反映Struts2慢，确实，Struts2是慢，很慢！慢在哪儿？很大一部分因素是UI这层引起的，特别是使用了过多的Struts2的tag，并使用了ajax theme。但是，如果我们放弃了Struts2的笨拙的UI，Result只输出XML或JSON，UI则使用标准的HTML+CSS，使用JS组件（DOJO、Adobe Spry Framework、YUI-Ext等）来操作Struts2的输出数据，情况将会如何？我们会得到一个高性能、高可配的、UI和应用服务器的职责分割更为明确、合理的、更易于静态化部署的开发组合。<br />
这似乎是阉割了Struts2，但是这样阉割过的Struts2摆脱了性能低下的包袱，更轻、更现代化。<br />
有些扯远了，言归正传，不管是让Struts2输出XML或JSON，还是输出页面，我们都有办法根据项目的规则写一个Result，将它配到global-results中，从而大大减少Result的配置。<br />
假设我们让Struts2只输出JSON，有个jsonplugin可以做这件事。使用JsonResult时，不再需要知道页面的位置、名称等信息，它仅仅是数据输出，那么我们就可以将这个Result配成全局的，大部分Action将不再需要Result的配置。<br />
作为示例，我假设我的例子中输出的两个html页面（success.html和error.html）是JSON，我们看看怎么免去我例子中的两个Action的Result注解。<br />
首先，我们删去ListPeopleAction和ListGroupAction两个Action的注解，并修改struts.xml文件，加入：<br />
<div class="code_title">xml 代码</div>
<div class="dp-highlighter">
<div class="bar">&nbsp;</div>
<ol class="dp-xml" start="1">
    <li class="alt"><span><span class="tag">&lt;</span><span class="tag-name">package</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;demo-default&quot;</span><span>&nbsp;</span><span class="attribute">extends</span><span>=</span><span class="attribute-value">&quot;struts-default&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span><span class="tag">&lt;</span><span class="tag-name">global-results</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span><span class="tag">&lt;</span><span class="tag-name">result</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;success&quot;</span><span class="tag">&gt;</span><span>/success.html</span><span class="tag">&lt;/</span><span class="tag-name">result</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span><span class="tag">&lt;/</span><span class="tag-name">global-results</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span><span class="tag">&lt;/</span><span class="tag-name">package</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
</ol>
</div>
<br />
<font color="#ff0000">请记住这只是一个示例，为了方便，我没在项目中加入jsonplugin来作真实的演示，我只是假设这个success是json输出，读者可以自行使用jsonplugin来作实验。</font><br />
<br />
现在，离成功不远了，但是项目仍然不能正常运行。我们的Action返回success，但并不会匹配到global-results中配置。为什么呢？因为，我们这里是把global-results配置到&quot;demo-default&quot;这个package下的，而Struts2根据actionPackages找到的Action不会匹配到这个package上。解决办法也很简单，还记得上面讲到的Parent Package吧？给Action加个注解，指定ParentPackage为&quot;demo-default&quot;。但这样可不是我喜欢的，其实有更好的办法，我们在struts.xml中加个constant就好了：<br />
<div class="code_title">xml 代码</div>
<div class="dp-highlighter">
<div class="bar">&nbsp;</div>
<ol class="dp-xml" start="1">
    <li class="alt"><span><span class="tag">&lt;</span><span class="tag-name">constant</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;struts.configuration.classpath.defaultParentPackage&quot;</span><span>&nbsp;</span><span class="attribute">value</span><span>=</span><span class="attribute-value">&quot;demo-default&quot;</span><span>&nbsp;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span></span></li>
</ol>
</div>
<br />
现在，大功告成！运行RunJetty来测试下吧！你可以访问/app/person/listPeople，可以访问/app/group/listGroup，而所有的配置仅仅是web.xml和struts.xml中的几行，我们的Java代码中也没有加注解。如果再加上几百个Action呢？配置仍然就这几行。<br />
可是，某些Action确实需要配置怎么办？对这些Action，你可以加注解，也可以针对这些Action来写些XML配置。一个项目中，大部分Action的配置是可以遵从一定规则的，可以使用规则来简化配置，只有少部分需要配置，这就是COC。<br />
<br />
<font color="#ff0000">注：</font>附件demo-struts-annotations.zip是使用注解实现零配置的示例代码，附件demo-struts-coc.zip是使用global-results后的示例代码。<br />
另外，我以前写过一篇文章《<a href="http://www.javaeye.com/topic/110934">改写Restful2ActionMapper让Struts2支持REST风格的URL映射</a>》，这里所说的零配置并不适用于支持REST。也就是说，你要用REST风格的URL映射，你就必须配置。不过还好，使用REST风格后，配置并不复杂。<br />
另外我见到不少人使用Spring来配置和管理Action，其实完全没有必要！设置struts.objectFactory 等于spring就可以了，如果在Action中有setService1，这个service1在Spring中有配置的话，它会自动注入的。Javaeye论坛中早有相关的讨论。<br />
<br />
参考：<br />
&nbsp;&nbsp;&nbsp; http://struts.apache.org/2.x/docs/zero-configuration.html<br />
&nbsp;&nbsp;&nbsp; http://struts.apache.org/2.x/docs/zero-configuration-scanning.html
          <br/>
          <span style="color:red;">
            <a href="http://andlu.javaeye.com/blog/112675#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 15 Aug 2007 22:21:25 +0800</pubDate>
        <link>http://andlu.javaeye.com/blog/112675</link>
        <guid>http://andlu.javaeye.com/blog/112675</guid>
      </item>
      <item>
        <title>改写Restful2ActionMapper让Struts2支持REST风格的URL映射</title>
        <author>andlu</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://andlu.javaeye.com">andlu</a>&nbsp;
          链接：<a href="http://andlu.javaeye.com/blog/110934" style="color:red;">http://andlu.javaeye.com/blog/110934</a>&nbsp;
          发表时间: 2007年08月10日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          [摘要]: 介绍如何改写Struts2的Restful2ActionMapper来支持REST风格的URL映射。<br />
<br />
<font color="#ff0000" style="background-color: rgb(204, 204, 204);"><strong>Note: </strong></font><span style="background-color: rgb(204, 204, 204);">不久前写了一篇文章《使用Restful2ActionMapper让Struts2支持REST风格的URL映射》，但后来发现有些不对，Struts2的Restful2ActionMapper并不按我想的那样运行。因为在我的实验项目中，我是改写了这个Restful2ActionMapper的。Struts2自己带的Restful2ActionMapper稍嫌复杂，而且我对它的有些地方的处理不甚满意，所以自己写了一个，</span><span style="background-color: rgb(204, 204, 204);">没有使用Struts2自己的Restful2ActionMapper进行调试</span><span style="background-color: rgb(204, 204, 204);">。特此向大家道歉，并修正该文档。另外，我对我改写的这个Restful2ActionMapper</span><span style="background-color: rgb(204, 204, 204);">的代码</span><span style="background-color: rgb(204, 204, 204);">进行了一些删减调整，将代码附于文后。</span>
<h2><font size="4">一、概述</font></h2>
REST是由 Roy Fielding 在他的论文《Architectural Styles and the Design of Network-based Software Architectures》中提出的一个术语。关于REST，请参考：http://www.redsaga.com/opendoc/REST_cn.pdf<br />
在REST的定义中，一个Web应用总是使用固定的URI向外部世界呈现（或者说暴露）一个资源，并使用不同的HTTP请求方法来处理对资源的CRUD（创建、读取、更新和删除）操作。除了我们所熟知的GET和POST这两种HTTP请求方法，HTTP还有HEAD、PUT、DELETE等请求方法。我们在Web应用中处理来自客户端的请求时，通常只考虑GET和POST，并使用某种URL映射将URL映射到对资源的某种操作。而REST架构风格则要求使用HTTP的GET、POST、PUT、DELETE来分别表示资源的读取、创建、更新、删除，而URI保持不变。举例来说，/article/2007/8/a001这个URI表示一篇文章，表示形式为：/article/{year}/{month}/{id}，对这个资源的CRUD操作如下（下面的表示形式中，我省去了http://host/context/namespace这样的前缀）：<br />
&nbsp; 读取：GET /article/2007/8/a001<br />
&nbsp; 创建：POST /article/2007/8/a001<br />
&nbsp; 更新：PUT /article/2007/8/a001<br />
&nbsp; 删除：DELETE /article/2007/8/a001<br />
如果我们用传统的struts或webwork的开发方法，我们可能会定义一个ArticleAction，定义好CRUD的method，并使用不同的URI映射来表示这几种操作。比如，我们可能会使用这样的URI来读取article：/getArticle.action?year=2007&amp;month=8&amp;id=a001，并使用这样的URI来删除article：/deleteArticle.action?year=2007&amp;month=8&amp;id=a001，或者，把这几种操作用相同形式的URI来表示：/article.action?method=get&amp;year=2007&amp;month=8&amp;id=a001。显然，REST风格的URI表示更友好。<br />
Struts2和Webwork2都带了一个RestfulActionMapper来支持REST风格的URI映射，但是它的功能太弱了，表现形式也很呆板。Struts2（我使用的是Struts 2.0.9）中还有一个Restful2ActionMapper，可以更好地支持REST风格。<br />
从struts2的官方文档中可以找到关于Restful2ActionMapper的说明: Improved restful action mapper that adds several ReST-style improvements to action mapping, but supports fully-customized URL's via XML.<br />
我查看了Restful2ActionMapper的源码，对它有些地方的处理有异议，所以改写了这个类。以下的配置中，请使用文后附上的Restful2ActionMapper代替Struts2原来的类。<br />
<br />
<h2><font size="4">二、配置和使用</font></h2>
现在，我们配置struts2使它使用Restful2ActionMapper。在Web项目中，修改struts.properties文件（它最终会发布到你的web应用的WEB-INF/classes目录中）：<br />
<font color="#ff0000">struts.mapper.class=org.apache.struts2.dispatcher.mapper.Restful2ActionMapper</font><br />
struts.enable.SlashesInActionNames=true<br />
当然，你也可以在struts.xml里进行配置，请参考struts2的相关文档。<br />
<font color="#ff0000">（这里，请将struts.mapper.class这行修改为使用我修改后的Restful2ActionMapper）</font><br />
<br />
<span style="background-color: rgb(255, 255, 255);">　　这里有个小建议，许多人在WEB-INF/web.xml里对struts2的配置是让struts2处理所有扩展名为action的url，也就是设置url-pattern为*.action。</span><span style="background-color: rgb(255, 255, 255);"><filter-mapping></filter-mapping></span> <span style="background-color: rgb(255, 255, 255);"></span><span style="background-color: rgb(255, 255, 255);">我的建议是，不要使用扩展名来作为url-pattern，使用基于路径的匹配形式会更好，我一般是使用&ldquo;/app/*&rdquo;作为url-pattern。至于扩展名，我一般是在struts.properties文件中指定：</span><br style="background-color: rgb(255, 255, 255);" />
<span style="background-color: rgb(255, 255, 255);">struts.action.extension=html,xml,json</span><br style="background-color: rgb(255, 255, 255);" />
<span style="background-color: rgb(255, 255, 255);">　　或者，不要扩展名：</span><br style="background-color: rgb(255, 255, 255);" />
<span style="background-color: rgb(255, 255, 255);">struts.action.extension=</span><br style="background-color: rgb(255, 255, 255);" />
<span style="background-color: rgb(255, 255, 255);">　　不过，这些都是题外话。</span><br />
<br />
现在，以上面讲到的article为例，我们定义ArticleAction。按照Restful2ActionMapper的规则，URL与method的对应关系如下：<br />
&nbsp; GET /article =&gt; public String index(); 资源索引；<br />
&nbsp; GET /article/2007/8/a001 =&gt; public String view(); 对应于读取操作；<br />
&nbsp; POST /article/2007/8/a001 =&gt; public String create(); 创建资源；<br />
&nbsp; PUT /article/2007/8/a001 =&gt; public String update(); 更新资源；<br />
&nbsp; DELETE /article/2007/8/a001 =&gt; public String remove(); 删除资源；<br />
&nbsp; GET /article/2007/8/a001!edit =&gt; public String edit(); 请求编辑资源，和REST的四种操作没有对应关系；<br />
&nbsp; GET /article/!editNew =&gt; public String editNew(); 请求编辑新资源，和REST的四种操作没有对应关系。<br />
&nbsp; 后两种方式似乎和REST没什么关系，但为传统的Web应用开发提供了方便。比如edit()，服务器返回一个表单页面。但是，如果我们让应用服务器只返回xml或json，那么这个edit()是可以不要的，有读取操作就够了。(也许把view方法改为read更贴切点)。<br />
按照这些规则，我们在ArticleAction中定义view()、create()、update()、remove()等method，并在这个action中定义year、month、id的getter/setter方法：<br />
<div class="code_title">java 代码</div>
<div class="dp-highlighter">
<div class="bar">&nbsp;</div>
<ol class="dp-j" start="1">
    <li class="alt"><span><span class="keyword">package</span><span>&nbsp;app;&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;ArticleAction&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">private</span><span>&nbsp;String&nbsp;year,&nbsp;month,&nbsp;id;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;...&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;getter/setter&nbsp;methods&nbsp;<span class="keyword">for</span><span>&nbsp;year,month,id&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;...&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;String&nbsp;view()&nbsp;{&nbsp;...&nbsp;}&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;String&nbsp;index()&nbsp;{&nbsp;...&nbsp;}&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;String&nbsp;create()&nbsp;{&nbsp;...&nbsp;}&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;String&nbsp;update()&nbsp;{&nbsp;...&nbsp;}&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;String&nbsp;remove()&nbsp;{&nbsp;...&nbsp;}&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>}&nbsp;&nbsp;</span></li>
</ol>
</div>
<br />
然后我们需要配置这个action，使它能与形如/article/{year}/{month}/{id}的URL对应起来。我们在相应的struts2的action配置文件中加入如下几行：<br />
<div class="code_title">xml 代码</div>
<div class="dp-highlighter">
<div class="bar">&nbsp;</div>
<ol class="dp-xml" start="1">
    <li class="alt"><span><span class="tag">&lt;</span><span class="tag-name">action</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;article/*/*/*&quot;</span><span>&nbsp;</span><span class="attribute">className</span><span>=</span><span class="attribute-value">&quot;app.ArticleAction&quot;</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="tag">&lt;</span><span class="tag-name">param</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;year&quot;</span><span class="tag">&gt;</span><span>{1}</span><span class="tag">&lt;/</span><span class="tag-name">param</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="tag">&lt;</span><span class="tag-name">param</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;month&quot;</span><span class="tag">&gt;</span><span>{2}</span><span class="tag">&lt;/</span><span class="tag-name">param</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="tag">&lt;</span><span class="tag-name">param</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;id&quot;</span><span class="tag">&gt;</span><span>{3}</span><span class="tag">&lt;/</span><span class="tag-name">param</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="tag">&lt;</span><span class="tag-name">result</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">&quot;...&quot;</span><span>&nbsp;</span><span class="attribute">type</span><span>=</span><span class="attribute-value">&quot;...&quot;</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span><span class="tag">&lt;/</span><span class="tag-name">action</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
</ol>
</div>
<param /><span class="tag"></span><span></span>          <br />
OK！现在已经可以使用这个action了。当然，这还需要浏览器客户端的支持。当你的客户端以GET来请求/article/2007/8/a001时，struts2就会调用ArticleAction的view方法，而PUT请求则会对应到update方法，DELETE请求会对应到remove方法...<br />
但是，如果你的客户端只支持GET和POST怎么办？Restful2ActionMapper的文档中提到：To simulate the HTTP methods PUT and DELETE, since they aren't supported by HTML, the HTTP parameter &quot;__http_method&quot; will be used.对于只支持GET和POST的传统网页，我们可以增加一个&quot;__http_method&quot;参数来模拟PUT和DELETE，比如：POST /article/2007/8/a001&amp;__http_method=DELETE。随着Javascript和Ajax框架的发展，我们已经可以使用PUT和DELETE等方法。Ajax使用XmlHttpRequest进行操作时，在发送请求之前，可以通过设置RequestType的方式来完成对请求方法的设定。<br />
<br />
<h2><font size="4">三、不足之处</font></h2>
Restful2ActionMapper对REST风格的支持是不完全的。在REST风格中，我们可以使用同一个URI来获取同一个资源的多种表现形式。在发送HTTP请求时，只要我们在请求头中指定一个Accept参数，那么服务器就可以通过判断该参数来决定返回什么类型的数据。如果Accept为text/xml，服务器会返回xml格式的数据，如果Accept为text/json，则会返回json格式的数据，但URI是固定的。而Restful2ActionMapper只是作了URI的映射，并没有考虑返回数据的格式问题。要让struts2支持完全的REST风格，我们不得不对它进行改造，或者，等待它的改进。<br />
另外，Restful2ActionMapper所定义的URL映射规则也有一个小小的&ldquo;陷阱&rdquo;。比如，GET /user/1表示读取id为1的user，但按照Restful2ActionMapper的定义，/user/new会对应到action的editNew方法，如果这个&quot;new&quot;就是某个用户的id呢？为了避开这个陷阱，我宁愿使用/user/!editNew这种丑陋的形式。事实上，随着客户端技术的发展，我们完全可以不使用editNew方法而构造输入页面，然后向服务器发送POST来创建资源。同样，edit方法也不是必要的。<br />
（注：我修改后的Restful2ActionMapper去除了/user/new这种形式的映射）<br />
<br />
<h2><font size="4">四、其它</font></h2>
有个struts2的插件，叫jsonplugin，可以让struts2很方便地支持json输出。而Adobe Spry Framework、YUI-ext、DOJO等都能很好地支持json，并能很好地支持HTTP的各种请求方法。我推荐struts2的用户使用jsonplugin和Adobe Spry Framework或YUI-ext(或其它UI Framework)。Struts2只输出json格式的结果（最好还能输出xml），而UI和数据装配交给Adobe Spry/YUI-ext等去做。这样的组合会让你更好更方便地使用REST风格。<br />
<br />
<h2>五、修改后的Restful2ActionMapper</h2>
这里我附上修改后的Restful2ActionMapper，大家可以在此基础上进行扩充。比如，我前面提到Restful2ActionMapper不能根据Accept请求头来返回不同格式的数据，其实也是可以进行改进的。我看到已经有人在读过我这篇文章后提出一种方案，类似于这样的：<br />
/user/a001/xml =&gt; 返回xml格式的result<br />
/user/a001/json =&gt; 返回json格式的result<br />
/user/a001/...<br />
这是一种办法，另外，根据url的扩展名来做，也是一种办法。但是这都不是好方案！我前面已经提过，按照REST风格，一个Web应用总是使用固定的URI向外部世界呈现（或者说暴露）一个资源，而前面这两种方案只是使URL友好点而已，并不真正符合REST风格。当然，这样也不错了，也是不错的方案，其实ROR中也有类似的做法。<br />
但我们还有更好的方案，我提个思路，然后大家自行对Restful2ActionMapper进行改进：<br />
在Action中可以设置一个consumeMime属性，并写好对应的getter/setter方法。在Restful2ActionMapper返回mapping之前，提取request的Accept头信息，然后将该信息放到mapping.params之中。action的各个method最后只返回consumeMime，这样就可以在action的配置文件中按consumeMime来配置result了。<br />
下面，附上修改后的Restful2ActionMapper代码：<br />
<div class="code_title">java 代码</div>
<div class="dp-highlighter">
<div class="bar">&nbsp;</div>
<ol class="dp-j" start="1">
    <li class="alt"><span><span class="keyword">import</span><span>&nbsp;com.opensymphony.xwork2.config.ConfigurationManager;&nbsp;&nbsp;</span></span></li>
    <li class=""><span><span class="keyword">import</span><span>&nbsp;javax.servlet.http.HttpServletRequest;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span><span class="keyword">import</span><span>&nbsp;org.apache.commons.logging.Log;&nbsp;&nbsp;</span></span></li>
    <li class=""><span><span class="keyword">import</span><span>&nbsp;org.apache.commons.logging.LogFactory;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span><span class="keyword">import</span><span>&nbsp;org.apache.struts2.dispatcher.mapper.ActionMapping;&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;Restful2ActionMapper&nbsp;</span><span class="keyword">extends</span><span>&nbsp;DefaultActionMapper&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">protected</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">final</span><span>&nbsp;Log&nbsp;LOG&nbsp;=&nbsp;LogFactory&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.getLog(Restful2ActionMapper.<span class="keyword">class</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">final</span><span>&nbsp;String&nbsp;HTTP_METHOD_PARAM&nbsp;=&nbsp;</span><span class="string">&quot;__http_method&quot;</span><span>;&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">private</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">final</span><span>&nbsp;</span><span class="keyword">byte</span><span>&nbsp;HTTP_METHOD_GET&nbsp;=&nbsp;</span><span class="number">1</span><span>;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">private</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">final</span><span>&nbsp;</span><span class="keyword">byte</span><span>&nbsp;HTTP_METHOD_POST&nbsp;=&nbsp;</span><span class="number">2</span><span>;&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">private</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">final</span><span>&nbsp;</span><span class="keyword">byte</span><span>&nbsp;HTTP_METHOD_PUT&nbsp;=&nbsp;</span><span class="number">3</span><span>;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">private</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">final</span><span>&nbsp;</span><span class="keyword">byte</span><span>&nbsp;HTTP_METHOD_DELETE&nbsp;=&nbsp;</span><span class="number">4</span><span>;&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;Restful2ActionMapper()&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setSlashesInActionNames(<span class="string">&quot;true&quot;</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">/*</span>&nbsp;</span></li>
    <li class=""><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;(non-Javadoc)</span>&nbsp;</span></li>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span>&nbsp;</span></li>
    <li class=""><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@see&nbsp;org.apache.struts2.dispatcher.mapper.ActionMapper#getMapping(javax.servlet.http.HttpServletRequest)</span>&nbsp;</span></li>
    <li class="alt"><span><span class="comment">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span><span>&nbsp;ActionMapping&nbsp;getMapping(HttpServletRequest&nbsp;request,&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ConfigurationManager&nbsp;configManager)&nbsp;{&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;(!isSlashesInActionNames())&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">throw</span><span>&nbsp;</span><span class="keyword">new</span><span>&nbsp;IllegalStateException(&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">&quot;This&nbsp;action&nbsp;mapper&nbsp;requires&nbsp;the&nbsp;setting&nbsp;'slashesInActionNames'&nbsp;to&nbsp;be&nbsp;set&nbsp;to&nbsp;'true'&quot;</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ActionMapping&nbsp;mapping&nbsp;=&nbsp;<span class="keyword">super</span><span>.getMapping(request,&nbsp;configManager);&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;(mapping&nbsp;==&nbsp;</span><span class="keyword">null</span><span>)&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;</span><span class="keyword">null</span><span>;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;actionName&nbsp;=&nbsp;mapping.getName();&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;((actionName&nbsp;==&nbsp;</span><span class="keyword">null</span><span>)&nbsp;||&nbsp;(actionName.length()&nbsp;==&nbsp;</span><span class="number">0</span><span>))&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;mapping;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;If&nbsp;a&nbsp;method&nbsp;hasn't&nbsp;been&nbsp;explicitly&nbsp;named,&nbsp;try&nbsp;to&nbsp;guess&nbsp;using</span><span>&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//&nbsp;ReST-style&nbsp;patterns</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;(mapping.getMethod()&nbsp;!=&nbsp;</span><span class="keyword">null</span><span>)&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;mapping;&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">int</span><span>&nbsp;lastSlashPos&nbsp;=&nbsp;actionName.lastIndexOf('/');&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;(lastSlashPos&nbsp;==&nbsp;-</span><span class="number">1</span><span>)&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;mapping;&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;requestMethod&nbsp;=&nbsp;request.getMethod();&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;httpMethodParam&nbsp;=&nbsp;request.getParameter(HTTP_METHOD_PARAM);&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">byte</span><span>&nbsp;requestMethodCode&nbsp;=&nbsp;</span><span class="number">0</span><span>;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;(</span><span class="string">&quot;PUT&quot;</span><span>.equalsIgnoreCase(requestMethod))&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;requestMethodCode&nbsp;=&nbsp;HTTP_METHOD_PUT;&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">else</span><span>&nbsp;</span><span class="keyword">if</span><span>&nbsp;(</span><span class="string">&quot;DELETE&quot;</span><span>.equalsIgnoreCase(requestMethod))&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;requestMethodCode&nbsp;=&nbsp;HTTP_METHOD_DELETE;&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">else</span><span>&nbsp;</span><span class="keyword">if</span><span>&nbsp;(</span><span class="string">&quot;PUT&quot;</span><span>.equalsIgnoreCase(httpMethodParam))&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;requestMethodCode&nbsp;=&nbsp;HTTP_METHOD_PUT;&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">else</span><span>&nbsp;</span><span class="keyword">if</span><span>&nbsp;(</span><span class="string">&quot;DELETE&quot;</span><span>.equalsIgnoreCase(httpMethodParam))&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;requestMethodCode&nbsp;=&nbsp;HTTP_METHOD_DELETE;&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">else</span><span>&nbsp;</span><span class="keyword">if</span><span>&nbsp;(</span><span class="string">&quot;GET&quot;</span><span>.equalsIgnoreCase(requestMethod))&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;requestMethodCode&nbsp;=&nbsp;HTTP_METHOD_GET;&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">else</span><span>&nbsp;</span><span class="keyword">if</span><span>&nbsp;(</span><span class="string">&quot;POST&quot;</span><span>.equalsIgnoreCase(requestMethod))&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;requestMethodCode&nbsp;=&nbsp;HTTP_METHOD_POST;&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;(requestMethodCode&nbsp;==&nbsp;HTTP_METHOD_GET)&nbsp;{&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>&nbsp;(lastSlashPos&nbsp;==&nbsp;actionName.length()&nbsp;-&nbsp;</span><span class="number">1</span><span>)&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mapping.setMethod(<span class="string">&quot;index&quot;</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">else</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mapping.setMethod(<span class="string">&quot;view&quot;</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span class="keyword">else</span><span>&nbsp;</span><span class="keyword">if</span><span>&nbsp;(requestMethodCode&nbsp;==&nbsp;HTTP_METHOD_POST)&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mapping.setMethod(<span class="string">&quot;create&quot;</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">else</span><span>&nbsp;</span><span class="keyword">if</span><span>&nbsp;(requestMethodCode&nbsp;==&nbsp;HTTP_METHOD_PUT)&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mapping.setMethod(<span class="string">&quot;update&quot;</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">else</span><span>&nbsp;</span><span class="keyword">if</span><span>&nbsp;(requestMethodCode&nbsp;==&nbsp;HTTP_METHOD_DELETE)&nbsp;&nbsp;</span></span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mapping.setMethod(<span class="string">&quot;remove&quot;</span><span>);&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>&nbsp;mapping;&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span></li>
    <li class=""><span>}&nbsp;&nbsp;</span></li>
</ol>
</div>
          <br/>
          <span style="color:red;">
            <a href="http://andlu.javaeye.com/blog/110934#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 10 Aug 2007 15:51:17 +0800</pubDate>
        <link>http://andlu.javaeye.com/blog/110934</link>
        <guid>http://andlu.javaeye.com/blog/110934</guid>
      </item>
  </channel>
</rss>