Struts2的零配置和REST插件

和朋友共同开发一个小项目中用到了RESTful风格,在这里总结一下。

首先在Struts2工程中导入所需的jar包

ezmorph-1.0.3.jar
json-lib-2.1-jdk15.jar
struts2-convention-plugin-2.2.1.1.jar
struts2-rest-plugin-2.2.1.1.jar
xstream-1.2.2.jar

以上这些包是实现零配置和REST所必须的jar,缺一不可。

Struts2的Convention插件的主要特点是“约定优于配置”,可能是受到了Ruby on rails框架的启发,Struts2也借鉴了这个创意,看文字中的意思就可以明白,只要我们按照“约定”来开发,就可以摒弃繁琐的XML文件,也就是说,action等类配置完全不必写在struts.xml文件中了。

那么,“约定”又是什么呢?

其实,只要把零配置的jar文件拷贝到工程中WEB-INF/lib文件夹下,就自动实现了零配置。对于Struts2而言,它会自动在你创建的action、actions、struts、struts2这四个包下自动搜索,只要满足2个条件,Struts2就会认为包里的类是Action类,哪2个条件呢?一是:实现了com.opensymphony.xwork2.Action接口的类;二是:类名以“Action”结尾的类。

当然,以上这两个条件是Struts2的默实现,你也可以按自己的想法来做出改变,比如可以搜索以“Controller”结尾的类,怎么改呢?后面会说到。

当Struts2按约定找到了这些符合条件的类以后,就会自动部署这些Action,但在不同的包结构下,访问这些Action的URL也是不同的,请看下面的表格举例:

 

访问URL
org.crazyit.actions.LoginAction映射到/
com.test.action.abc.UserAction映射到/abc
org.crazyit.struts2.wage.hr.AddEmployeeAction映射到/wage/hr
org.crazyit.struts.auction.bid.BidAction映射到/auction/bid








 

 

 

看到上面的表格是不是了解了呢?Struts2总是以action、actions、struts、struts2包为根包,来映射成对应的URL访问路径。


而访问Action的名字,也应遵循两个规则,第一:如果类名包含Action后缀,那么把Action后缀去掉;第二:将以骆驼命名法的类名转成中画线写法,所有的字母都小写,单词之间用中画线分割。比如:

 

类名映射
LoginAction/login.action
GetBooks/get-books.action
AddEmployeeAction/add-employee.action


以上按照这些约定,Strtus2就会自动识别Action类,并正常访问它们了。编写Action则于从前未使用零配置插件一样,没有任何不同。


说完了Action,那么Result如何映射呢?因为很少有Action不返回逻辑视图的,零配置插件也提供了映射Result的约定。默认情况下,Result使用:action的URL + Result返回值 + 后缀 来做约定。例如当一个UserAction返回success时,Strus2就会查找user-success.jsp作为视图资源。而Struts2默认查找的地方在/WEB-INF/content目录下,那当然了,如果视图资源不是jsp文件,那也是一样的。比如LoginAction返回error,并且结果类型为FreeMarker,结果视图为login-error.ftl。


而JSP等视图资源也没有什么特别之处,到此为止,配置全部由约定完成,无需配置struts.xml文件。


但上面曾说过,如果要改变默认搜索包的名字,要怎样呢?只需在struts.xml文件中写入如下配置即可:

<constant name="struts.convention.action.suffix" value="Controller" />

查找以“Controller”结尾的类为Action类


<constant name="struts.convention.package.locators" value="org.demo" />

指定哪些包为搜索Action的根包


<constant name="struts.convention.exclude.packages" value="test" />

排除掉不搜索的包


还有很多可以改变Struts2的默认规则,其他可参阅文档。


而REST插件可以让Struts2实现RESTful风格的URL访问资源方式,具体REST风格是什么,可以参阅《RESTful Webservice》这本书。

其实Struts2本质上是一个MVC框架,而REST插件是将原本的URL转换成RESTful风格的URL而已, REST插件中RestActionMapper负责接收参数,把HTTP的请求方式分别用7个方法来做出处理:

 

HTTP方法URI调用Action的方法请求参数
GET/bookindex 
POST/bookcreate 
PUT/book/2updateid=2
DELETE/book/2destoryid=2
GET/book/2showid=2
GET/book/2/editeditid=2
GET/book/neweditNew 


而使用了REST插件之后,Action类就不使用execute()方法来处理用户请求了,而是上面的7个方法来实现


这样,如果按RESTful风格对应的HTTP请求如下:
GET /article 对应index()方法,用于查询操作;
GET /article/2007/8/a001对应view()方法,对应于读取操作(查询单条);
POST /article/2007/8/a001对应create()方法,用于新建操作;
PUT /article/2007/8/a001对应update()方法,用于更新操作;
DELETE /article/2007/8/a001对应remove()方法,用于删除操作;
GET /article/2007/8/a001!edit对应edit(); 请求编辑,和REST的四种操作没有对应关系
GET /article/new 或 GET /article/!editNew对应editNew()方法,请求编辑新资源,和REST的四种操作没有对应关系。


还是通过一个例子来看一下,首先建立Action类,依然继承自ActionSuport


Java代码   收藏代码
  1. @Namespace("/users/operate-history")  
  2. @Results(@Result(name="success", location="../../job-collection.jsp"))  
  3. public class JobCollectionAction extends ActionSupport implements ServletResponseAware{  
  4.       
  5.     private String id;  
  6.     public String getId() {  
  7.         return id;  
  8.     }  
  9.   
  10.     @Autowired  
  11.     private IJobCollectionService jobCollectionService;  
  12.     @Autowired  
  13.     private IJobService jobService;  
  14.       
  15.     private HttpServletResponse response;  
  16.       
  17.     private PageCond pageCond = new PageCond();  
  18.   
  19.     public PageCond getPageCond() {  
  20.         return pageCond;  
  21.     }  
  22.   
  23.     public void setPageCond(PageCond pageCond) {  
  24.         this.pageCond = pageCond;  
  25.     }  
  26.   
  27.     //  参数,职位id  
  28.     private String code = new String();  
  29.       
  30.     protected void mySearch() throws Throwable{  
  31. //      初始化session中保存的权限信息  
  32.         Authentication auth = SecurityContextHolder.getContext().getAuthentication();  
  33.         UserDetail curUserDetail = (UserDetail) auth.getPrincipal();  
  34.         User currentUser = (User) curUserDetail.getCurUserMap().get("user");  
  35.         List<JobCollection> list= jobCollectionService.search(currentUser, pageCond.getBeginNum(), pageCond.getLength());  
  36.         pageCond.setCount(jobCollectionService.count());  
  37.         System.out.println("数量:"+list.size());  
  38.         HttpServletRequest req= ServletActionContext.getRequest();  
  39.         req.setAttribute("JobCollectionList", list);  
  40.         req.setAttribute("pageCond",pageCond);  
  41.     }  
  42.       
  43.     public String index() throws Throwable{  
  44.           
  45.         System.out.println("JobCollectionAction-index");  
  46.         this.mySearch();  
  47.         return SUCCESS;  
  48.     }  
  49.     public String destroy() throws Throwable{  
  50.           
  51.         super.addActionMessage("删除id为:"+ id +"的数据");  
  52.         this.mySearch();  
  53.         return SUCCESS;  
  54.     }  
  55. }  
 

 

以上的类中index()方法用于处理不带参数的GET请求,其作用是查询出所有记录。
按以上的配置,使用http://localhost:8080/bee/users/operate-history/job-collection就可以触发该Action类中的index()方法(bee在这里是我的工程名),其中类使用了@Result注解的方式来作了说明,为什么使用了@Result而不按约定走,这主要是我在类中使用了命名空间,我把这个Action配置在了users/operate-history下,如果按约定,那么Struts2就会去WEB-INF/content/users/operate-history下去找jsp文件,这还要建立相应的目录,为了避免麻烦,我使用了注解改成直接在content目录下查找结果页面。
这样,调用了业务逻辑对象查询出数据,就可以跳转到jsp页面上遍历显示了。


而删除在这里要多说一下,因为现在并不支持DELETE和PUT两个操作,所以,我们要在表单中多传一个值来模拟这种方式,方法就是在你的<form>标记中加入一个隐藏域:<input type="hidden" name="_method" value="DELETE"/>,这样,在提交表单的时候,Struts2就会知道你当前的请求方式是DELETE,而执行destory()方法。更新也是一样的,加入PUT隐藏域就可以了。下面这个例子:

 

Html代码   收藏代码
  1. <table cellspacing="0" cellpadding="0" border="1">  
  2.     <tr>  
  3.         <th>序号</th><th>描述</th><th>发布日期</th><th>操作</th>  
  4.     </tr>  
  5.     <s:iterator var="i" value="#request.JobCollectionList" status="s">  
  6.     <s:form method="POST" theme="simple" action="users/operate-history/job-collection/%{#i.job.id}">  
  7.     <s:hidden name="_method" value="DELETE"/>  
  8.     <tr>  
  9.         <td><s:property value="#s.index+1"/></td>  
  10.         <td><s:property value="#i.job.jobDescription"/></td>  
  11.         <td><s:date name="#i.job.publishDate" format="yyyy-MM-dd" /></td>  
  12.         <td><s:submit value="删除"/></td>  
  13.     </tr>  
  14.     </s:form>  
  15.    </s:iterator>  
  16. </table>  
 

 

以上使用迭代标签将内容输出之后,动态的绑定了action属性:action="users/operate-history/job-collection/%{#i.job.id},目的就是传入相应的id,这样发出请求之后,Action类中的id属性就会赋予相应的值,并且加入了隐藏域以模拟DELETE请求。在destroy()方法中,取得id,就可以调用业务逻辑对象来进行删除记录的操作了。

其他请求大同小异,这里不再多说。

有意思的是,使用http://localhost:8080/bee/action-name/1/XXX这种请求方式,其实XXX可以是任何合法的名字,不仅仅只有editNew和edit,名字你可以自己定,Struts2会查找XXX为名字的方法来调用,比如请求http://localhost:8080/bee/test/1/abc,那么TestAction的public String abc()就会被调用。


注意:如果Controller的格式是驼峰的,那么请求路径要换成“-”,比如MyTestController,请求路径为my-test

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值