struts2中action实现Preparable接口

 最近在学习struts2,发现了很多惊喜,在设计上,确实很优秀,这两天在研究怎么做出一个好的架构,以后好用。
    今天看了包中的showcase例子,发现了一种新的配置action方法:
<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->        <action name="edit-*" class="org.apache.struts2.showcase.action.EmployeeAction">
      
<param name="empId">{1}</param>
            
<result>/empmanager/editEmployee.jsp</result>
            
<interceptor-ref name="crudStack">
                <
param name="validation.excludeMethods">execute</param>
            </
interceptor-ref>
        
</action>
    当时昏了一下,用过这么多MVC的框架,第一次看到action还可以用*配的,很牛啊,自己配了一个,不好使,找原因,发现这里的EmployeeAction实现了com.opensymphony.xwork2.Preparable接口,并实现了prepare()方法,再试,成功。但发现个问题,如果这个action中还有其它的控制处理,而配置文件用的不是这种*形式,比如我的例子:
<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--><action name="BookAction" class="com.sigon.crud.action.BookAction">
            
<result type="redirect">List.action</result>
        
</action>
<action name="Edit*" class="com.sigon.crud.action.BookAction" method="load">
            
<param name="isbn">{1}</param>
            
<result>Edit.jsp</result>
        
</action>
    这里两个action用的是一个action类文件,而且BookAction实现了上面所说的Preparable接口,这时就需要将BookAction中的isbn初始化,否则在页面调用BookAction时出空指针异常
    而在实际项目中,一个Action处理多个控制逻辑是家常便饭,如果还想用这种形式,就要在声明isbn时赋初值,本例中是
private String isbn = "2";
    很明显,不稳妥。所以如何取舍,颇为困扰。
    如果有哪位大侠有好的方法,敬请赐教。

Struts2的Preparable接口

分类: Struts2 940人阅读 评论(1)收藏举报

Struts2的Action在实现com.opensymphony.xwork2.Preparable接口后,就可以重写prepare()方法
此时在Action中,prepare()方法的执行点是在:setXxx()execute()的执行之前


比如需求:在执行Action的方法前,接收前台隐藏域传过来的值,再根据该值执行相应逻辑
如前台传过来ID,我们根据ID查找数据库对应的用户信息,再跳转到modify()中修改信息
但实际的运行过程中发现,通过Debug断点调试得知prepare()方法接收到的ID值是零
即前台隐藏域中的ID值没有传过来,事实上问题就出在默认的defaultStack拦截器栈
其实defaultStack无法接收prepare()需要的数据,而应借助paramsPrepareParamsStack拦截器栈
事实上使用prepare拦截器之前,应先调用params拦截器,prepare()才能接收到表单数据
基于这个思路,于是可以通过各种手段将params拦截器放置在prepare拦截器之前即可
比如将defaultStack中的所有拦截器拷贝struts.xml的我们自定义的myStack拦截器栈
再按照paramsPrepareParamsStack拦截器栈中的paramsprepare顺序修改二者位置即可

最近读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="**" 的形式似乎不行,不得以改成了"*_*"了,哈哈,要是能改成"^_^"就更好了
注:在struts.xml中增加<constant name="struts.enable.SlashesInActionNames" value="true" />,可以使用"*
    public String delete() throws Exception {
        log.info("delete the person");
        service.deletePerson(id);
        return SUCCESS;
    }

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

   
    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;
    }

   
    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中的方法名称给客户端,是否会带来安全性的问题

 

使用场景:
如果action针对每次请求都要执行一些相同的业务逻辑, 那么可以实现Preparable接口, 将预处理业务逻辑写在prepare()方法里


Preparable 接口定义:
public interface Preparable {
    void prepare() throws Exception;
}


prepare何时被执行:
prepare方法由PrepareInterceptor拦截器调用执行


com.opensymphony.xwork2.interceptor.PrepareInterceptor
    public String doIntercept(ActionInvocation invocation) throws Exception {
        Object action = invocation.getAction();

        if (action instanceof Preparable) {
            try {
                String[] prefixes;
                if (firstCallPrepareDo) {
                    prefixes = new String[] {ALT_PREPARE_PREFIX, PREPARE_PREFIX};
                } else {
                    prefixes = new String[] {PREPARE_PREFIX, ALT_PREPARE_PREFIX};
                }
                PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes);
            }
            catch (InvocationTargetException e) {
                // just in case there's an exception while doing reflection,
                // we still want prepare() to be able to get called.
                LOG.warn("an exception occured while trying to execute prefixed method", e);
            }
            catch (IllegalAccessException e) {
                // just in case there's an exception while doing reflection,
                // we still want prepare() to be able to get called.
                LOG.warn("an exception occured while trying to execute prefixed method", e);
            } catch (Exception e) {
                // just in case there's an exception while doing reflection,
                // we still want prepare() to be able to get called.
                LOG.warn("an exception occured while trying to execute prefixed method", e);
            }

            // 必须执行或初始化的一些业务操作 action prepare()方法
            if (alwaysInvokePrepare) {
                ((Preparable) action).prepare();
            }

        }

        return invocation.invoke();
    }


使用方法:

使用basicStack拦截器栈
<action name="list" class="org.apache.struts2.showcase.action.SkillAction" method="list">
 <result>/empmanager/listSkills.jsp</result>
 <interceptor-ref name="basicStack" />    <!-- 使用basicStack拦截器栈, 该拦截器栈在 struts-default.xml 已配置, 包括了prepare -->
</action>

(注: struts-default.xml  里定义的 basicStack拦截器栈

       <!-- Basic stack -->
            <interceptor-stack name="basicStack">
                <interceptor-ref name="exception"/>
                <interceptor-ref name="servletConfig"/>
                <interceptor-ref name="prepare"/>
                <interceptor-ref name="checkbox"/>
                <interceptor-ref name="multiselect"/>
                <interceptor-ref name="actionMappingParams"/>
                <interceptor-ref name="params">
                    <param name="excludeParams">dojo\..*,^struts\..*</param>
                </interceptor-ref>
                <interceptor-ref name="conversionError"/>
            </interceptor-stack>

)

或者直接指定prepare拦截器

<action name="list" class="org.apache.struts2.showcase.action.SkillAction" method="list">
 <result>/empmanager/listSkills.jsp</result>
 <interceptor-ref name="prepare" />   <!-- 直接指定prepare拦截器 -->
</action>

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭