之前做过不少的项目,所有的action只实现了一个execute()方法,也用到过Preparable接口,并没注意到它的具体用法。随着项目的需求的增加,按照以前的方法,每一个功能都需要一个action,这样势必会造成action类的大规模膨胀。所以决定采取action!method的形式,这样在一个action中可以包含很多方法,减少了action类的数量,也便于维护。
把crud方法放在一个action类中,就必定会涉及到一些数据准备的事情,所以用Preparable接口就再合适不过了,实现这个接口的prepare()方法,这个方法会在action类的所有方法执行前执行,另外我们也可以按照它的规则来写一些其它形式的prepare方法,例如aciton中有一个方法叫input(),那么我们可以实现一个prepareInput方法,这样在input()执行前,会执行prepareInput()方法。
好了,言归正传,我们有这样的一个action:
package ht.gisoa.action;
import ht.gisoa.model.Sysconfig;
import ht.gisoa.service.SysconfigManager;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import com.opensymphony.webwork.ServletActionContext;
import com.opensymphony.webwork.interceptor.ServletRequestAware;
import com.opensymphony.webwork.interceptor.SessionAware;
import com.opensymphony.xwork.ActionSupport;
import com.opensymphony.xwork.ModelDriven;
import com.opensymphony.xwork.Preparable;
import com.thoughtworks.xstream.XStream;
public class SystemConfigAction extends ActionSupport implements Preparable,SessionAware,ServletRequestAware,ModelDriven{
/**
*
*/
private Map session ;
private HttpServletRequest request;
private NamedParameterJdbcTemplate namedParameterJdbcTemplate = null;
private Double hignSpeed = 0.0;
private Double midSpeed = 0.0;
private Double lowSpeed = 0.0;
private static final long serialVersionUID = 1L;
private SysconfigManager sysconfigManager = null;
private Long id ;
private Sysconfig entity;
public void setId(Long id) {
this.id = id;
}
public String editSpeed() throws Exception{
Map<String,Sysconfig> configs = sysconfigManager.getSpeedConfig() ;
ServletActionContext.getRequest().setAttribute("configs", configs);
return this.SUCCESS;
}
public String input() throws Exception{
return "input";
}
public String list(){
List<Sysconfig> speedCollection = sysconfigManager.getSpeedList();
ServletActionContext.getRequest().setAttribute("speedCollection", speedCollection);
return "list";
}
public String save() throws Exception{
sysconfigManager.mergy(entity);
return "reload";
}
public String delete() throws Exception{
sysconfigManager.delete(id);
return "reload";
}
public SysconfigManager getSysconfigManager() {
return sysconfigManager;
}
public void setSysconfigManager(SysconfigManager sysconfigManager) {
this.sysconfigManager = sysconfigManager;
}
public Double getHignSpeed() {
return hignSpeed;
}
public void setHignSpeed(Double hignSpeed) {
this.hignSpeed = hignSpeed;
}
public Double getMidSpeed() {
return midSpeed;
}
public void setMidSpeed(Double midSpeed) {
this.midSpeed = midSpeed;
}
public Double getLowSpeed() {
return lowSpeed;
}
public void setLowSpeed(Double lowSpeed) {
this.lowSpeed = lowSpeed;
}
public void setSession(Map session) {
this.session = session;
}
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
public void prepare() throws Exception {
}
public void prepareModel() throws Exception {
if (id==null){
System.out.println("id=null");
entity = new Sysconfig();
entity.setKeyType(1L);
}else{
System.out.println("id=="+id);
entity = sysconfigManager.get(id);
}
}
public void prepareInput() throws Exception{
prepareModel();
}
public void prepareSave() throws Exception {
prepareModel();
}
public Object getModel() {
return entity;
}
}
这里要注意,要在spring中配置该action的作用域为prototype,否则,不同的方法之间会出现数据混乱的情况:
<bean id="systemconfig" class="ht.gisoa.action.SystemConfigAction" scope="prototype"> <property name="sysconfigManager"> <ref bean="SysconfigManager" /> </property> </bean>
sysconfig_input.jsp代码如下:
<%@ page language="java" pageEncoding="UTF-8" isELIgnored="false"%> <%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> </head> <body> <div id="msg"> ${msg} </div> <c:choose><c:when test="${ param.id == null}">创建</c:when><c:otherwise>修改</c:otherwise></c:choose>设置 <div id="main"> <form action="systemconfig!save.action"> <input type="text" name="model.keyName" value="${model.keyName}"/><br> <input type="text" name="model.keyValue" value="${model.keyValue}"/><br> <input type="hidden" name="model.id" value="${model.id}"/><br> <input type="hidden" name="model.keyType" value="1"/><br> <input type="submit" value="修改"/> </form> </div> <c:remove var="msg" scope="session"/> </body> </html>
我们编辑完信息,提交后,会执行action中的save方法,按道理,表单提交后,会将action中的model填充好数据,但是save方法之前又会执行prepareSave方法,从数据库里获取一次数据,这样不就把之前填充好的数据冲掉了吗?仔细想想也确实是这样的.
那么该如何解决这种矛盾呢,立即使出google大法,查了一下关于interceptor的资料,有这样的一个interceptor:paramsPrepareParamsStack,paramsPrepareParamsStack主要解决了ModelDriven和Preparable的配合问题,从字面上理解来说,这个stack的拦截器调用的顺序为:首先params,然后prepare,接下来modelDriven,最后再params。Struts 2.0的设计上要求modelDriven在params之前调用,而业务中prepare要负责准备model,准备model又需要参数,这就需要在prepare之前运行params拦截器设置相关参数,这个也就是创建paramsPrepareParamsStack的原因。流程如下:
1. params拦截器首先给action中的相关参数赋值,如id
2. prepare拦截器执行prepare方法,prepare方法中会根据参数,如id,去调用业务逻辑,设置model对象
3. modelDriven拦截器将model对象压入value stack,这里的model对象就是在prepare中创建的
4. params拦截器再将参数赋值给model对象
5. action的业务逻辑执行
我的xwork.xml中相关配置如下:
<action name="systemconfig" class="systemconfig"> <result name="list" type="dispatcher"> <param name="location">syscfg_speed_list.jsp</param> </result> <result name="reload" type="redirect"> <param name="location">systemconfig!list.action</param> </result> <result name="input" type="dispatcher"> <param name="location">syscfg_speed_input.jsp</param> </result> <interceptor-ref name="paramsPrepareParamsStack" /> <interceptor-ref name="modelParamsStack"></interceptor-ref> </action>
通过paramsPrepareParamsStack可以让流程更明确,代码更简洁,也更利于大家的交流。