也來寫個struts2 CURD的例子-Move CRUD Operations into the same Action

 最近讀starting struts2 online,裏面有一節Move CRUD Operations into the same Action,提供了Move CRUD Operations into the same Action大概的sample,於是進行了補充,記錄下來,以備使用。

一、思路

在這本書裏,lan roughley提到在結合preparable和ModenDriven攔截器實現Move CRUD Operations into the same Action,採用通配符的方式爲所有的crud只寫一個action配置,當然,這也要求相關文件的命名和目錄組織的時候要遵循一定的要求,示例如下:

 

<action name="*/*" method="{2}"class="com.infoq.actions.{1}Action">
   <result type="redirect">/{1}/view.action</result>
   <result name="view">/{1}/view.jsp</result>
   <result name="input">/{1}/edit.jsp</result>
   <result name="home">/{1}/home.jsp</result>
</action>


採用這種方式後,所有的action的url都將是  
 “/{model}/{method}.action”的形式,從層次上看也比較的合理,更重要的是可以大幅度減少action配置的copy and paste

二、實現方式

 具體工作的原理我就不多重複了,主要使用preparable和ModenDriven的攔截器,可以參考starting struts2 online,上面解釋得非常清楚,只是我在具體實現的時候,方向採用"*/*" 的形式似乎不行,不得以改成了"*_*"了,哈哈,要是能改成"^_^"就更好了 
注:在struts.xml中增加<constant name="struts.enable.SlashesInActionNames" value="true" />,可以使用"*/*"的方式

下面貼出具體的實現例子,同樣了爲了簡化,在service層面上採取ArrayList模擬的數據庫,下面貼代碼:

Action 配置文件

因爲要用preparable和modendriven,所以需要paramsPrepareParamsStack攔截器棧,下面會專門解釋paramsPrepareParamsStack

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="person" namespace="/person"
extends="struts-default">
<default-interceptor-ref name="paramsPrepareParamsStack" />
<!-- D?3¨¦person/add.action¦Ì¨¨url?¨¢11 -->
<action name="*_*" method="{2}"
class="com.work.action.person.{1}Action">
<result name="input">/pages/{1}/edit.jsp</result>
<result type="redirect">ListPersons.action</result>
</action>
<action name="ListPersons" class="com.work.action.person.ListPersons">
<result>/pages/Person/view.jsp</result>
</action>
<!-- Add actions here -->
</package>
</struts>

 PersonAction.java

package com.work.action.person;
import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.Preparable;
import com.work.action.BaseSupport;
import com.work.model.Person;
import com.work.service.PersonService;
import com.work.service.PersonServiceImpl;

public class PersonAction extends BaseSupport implements ModelDriven<Person>,
        Preparable {

    private static final long serialVersionUID = 8115088127593809818L;
       // 定義了作爲model的person,因爲使用了modeldriven,所以jsp中form的值會直接setter到person中     
    private Person person;
       // id 用來保存用戶的id,通過paramsPrepareParamsStack中的第一個param攔截器注入到action
    private String id;
    private PersonService service = new PersonServiceImpl();

    public void prepare() throws Exception {
        System.out.println(id);
        if (id == null || id.length() == 0)
            person = new Person();
        else
            person = service.find(id);
    }


    /**
     * delete
     */

    public String delete() throws Exception {
        log.info("delete the person");
        service.deletePerson(id);
        return SUCCESS;
    }


    /**
     * edit
     */

    public String edit() {
        return "input";
    }


    /**
     * add and update
     * 
     * @return
     * @throws Exception
     */

    public String update() throws Exception {
        if (id == null || id.length() == 0{
            log.info("add the person");
            service.addPerson(person);
        }
 else {
            log.info("update the person");
            service.updatePerson(person);
        }

        return SUCCESS;
    }


    /**
     * ??
     * 
     * @return
     */

    public String view() {
        return SUCCESS;
    }


    public Person getPerson() {
        return person;
    }


    public void setPerson(Person person) {
        this.person = person;
    }


    public PersonService getService() {
        return service;
    }


    public void setService(PersonService service) {
        this.service = service;
    }


    public String getId() {
        return id;
    }


    public void setId(String id) {
        this.id = id;
    }


    public Person getModel() {
        return person;
    }

}

 

PersonListAction.java  :

package com.work.action.person;

import java.util.List;

import com.work.action.BaseSupport;
import com.work.model.Person;
import com.work.service.PersonService;

public class PersonListAction extends BaseSupport {
    private static final long serialVersionUID = 1810482163716677456L;
    private List<Person> people;
    private PersonService service=new PersonServiceImpl(); ;

    public String execute() throws Exception {
        log.info("list persons");
        people = service.getAllPersons();
        return SUCCESS;
    }


    public List<Person> getPeople() {
        return people;
    }


    public void setPeople(List<Person> people) {
        this.people = people;
    }


    public PersonService getService() {
        return service;
    }


    public void setService(PersonService service) {
        this.service = service;
    }

}

paramsPrepareParamsStack

這裏需要說一下關鍵的paramsPrepareParamsStack攔截器,其中params攔截器出現了兩次,第一次位於servletConfig和prepare之前,更在modelDriven之前,因此會將http://localhost:8080/diseaseMS/person/Person_edit.action?id=202中的參數id注入到action中,而不是model中,之後prepare將根據這個id,從服務層提取model。

下面是paramsPrepareParamsStack的英文註釋:

 

An example of the params-prepare-params trick. This stack  is exactly the same as the defaultStack, except that it  includes one extra interceptor before the prepare interceptor:the params interceptor.
This is useful for when you wish to apply parameters directly to an object that you wish to load externally (such as a DAO or database or service layer), but can't load that object  until at least the ID parameter has been loaded. By loadingthe parameters twice, you can retrieve the object in the prepare() method, allowing the second params interceptor toapply the values on the object.

 


<interceptor-stack name="paramsPrepareParamsStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="params"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="params"/>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel</param>
</interceptor-ref>
</interceptor-stack>

 

PersonServiceImpl.java

package com.work.service;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import com.work.model.Person;

public class PersonServiceImpl implements PersonService {
    /**
     * 示例程序,沒有添加同步機制,多線程會有問題
     */

    private List<Person> personList;

    public PersonServiceImpl() {
        personList = new ArrayList<Person>();
        Person p1 = new Person("202""name1""beijing");
        Person p2 = new Person("203""name2""beijing");
        Person p3 = new Person("204""name3""tianjing");
        personList.add(p1);
        personList.add(p2);
        personList.add(p3);

    }


    public void addPerson(Person person) {
        if (person == null{
            throw new RuntimeException("add kong");
        }

        person.setId(getID(200));
        personList.add(person);
    }


    public void deletePerson(String personID) {
        int target = findLocation(personID);
        if (target != -1)
            personList.remove(target);
    }


    public List<Person> getAllPersons() {
        return personList;
    }


    public void updatePerson(Person person) {
        if (person == null{
            throw new RuntimeException("update kong");
        }

        int target = findLocation(person.getId());
        personList.remove(target);
        personList.add(person);
        
    }


    private int findLocation(String personID) {
        int target = -1;
        for (int i = 0; i < personList.size(); i++{
            if (personID.equals(personList.get(i).getId())) {
                target = i;
                break;
            }

        }

        return target;
    }


    public Person find(String personID) {
        Person person = null;
        int target = findLocation(personID);
        if (target != -1{
            person = personList.get(target);
        }

        return person;
    }

    
    private String getID(int round) {
        Random rand = new Random();
        int needed = rand.nextInt(round) + 1// 1-linesum+1
        return needed+"";
    }


}

下面就是jsp文件了,就只貼部分了:
edit.jsp:

<s:form action="Person_update.action" >
    <s:textfield label="your ID" name="id" readonly="true"/>
    <s:textfield label="Please enter your name" name="name" required="true"  />
    <s:textfield label="Please enter your homeaddr" name="homeAddr" required="true"/>    
    <s:submit />
</s:form> 


view.jsp

 

<%@ page contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<html>
<head>
<title>person view</title>
</head> 

<body>
<s:actionerror />
<table>
    <tr>
        <td>id</td>
        <td>name</td>
        <td>address</td>
        <td></td>
        <td></td>
    </tr>
    <s:iterator value="people">
        <tr>
            <td><s:property value="id" /></td>
            <td><s:property value="name" /></td>
            <td><s:property value="homeAddr" /></td>
            <td><s:url id="update" action="Person_edit.action" >
                <s:param name="id">
                    <s:property value="%{id}" /> 
                </s:param>
            </s:url> <s:a href="%{update}">修改</s:a>
            </td> 

            <td><s:url id="delete" action="Person_delete.action">
                <s:param name="id">
                    <s:property value="%{id}" />
                </s:param>
            </s:url> <s:a href="%{delete}">刪除</s:a>
            </td>
        </tr>
    </s:iterator>
</table>
<ul>
    <li><a href="<s:url action="Person_input"/>">Create a new person</a></li> 

</ul>
</body>
</html>

 

三、總結

優點:適用與crud比較的應用程序,大幅減少action的配置信息

其他:

1、也可以不實現modeldriven接口,只不過要在jsp中加上model.properity

2、這種方式在某種程度上透露了action中的方法名稱給客戶端,是否會帶來安全性的問題

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章