Struts之五 ModelDrivin &paramsPrepareParamsStack

1. Action 实现 ModelDriven 接口后的运行流程

1). 先会执行 ModelDrivenInterceptor 的 intercept 方法. 

public String intercept(ActionInvocation invocation) throws Exception {
    	//获取 Action 对象: EmployeeAction 对象, 此时该 Action 已经实现了 ModelDriven 接口
    	//public class EmployeeAction implements RequestAware, ModelDriven<Employee>
        Object action = invocation.getAction();

		//判断 action 是否是 ModelDriven 的实例
        if (action instanceof ModelDriven) {
        	//强制转换为 ModelDriven 类型
            ModelDriven modelDriven = (ModelDriven) action;
            //获取值栈
            ValueStack stack = invocation.getStack();
            //调用 ModelDriven 接口的 getModel() 方法
            //即调用 EmployeeAction 的 getModel() 方法
            /*
            public Employee getModel() {
				employee = new Employee();
				return employee;
			}
            */
            Object model = modelDriven.getModel();
            if (model !=  null) {
            	//把 getModel() 方法的返回值压入到值栈的栈顶. 实际压入的是 EmployeeAction 的 employee 成员变量
            	stack.push(model);
            }
            if (refreshModelBeforeResult) {
                invocation.addPreResultListener(new RefreshModelBeforeResult(modelDriven, model));
            }
        }
        return invocation.invoke();
    }

   
2). 执行 ParametersInterceptor 的 intercept 方法: 把请求参数的值赋给栈顶对象对应的属性. 若栈顶对象没有对应的属性, 则查询值栈中下一个对象对应的属性...

3). 注意: getModel 方法不能提供以下实现. 的确会返回一个 Employee 对象到值栈的栈顶. 但当前 Action 的 employee 成员变量却是 null. 
public Employee getModel() {
return new Employee();

}    

需要写成

public Employee getModel() {
employee = new Employee();
return new Employee();

}    


2. 使用 paramsPrepareParamsStack 拦截器栈后的运行流程

1). paramsPrepareParamsStack 和 defaultStack 一样都是拦截器栈. 而 struts-default 包默认使用的是defaultStack

2). 可以在 Struts 配置文件中通过以下方式修改使用的默认的拦截器栈

<default-interceptor-ref name="paramsPrepareParamsStack"></default-interceptor-ref>


3). paramsPrepareParamsStack 拦截器在于 
params -> modelDriven -> params

所以可以先把请求参数赋给 Action 对应的属性, 再根据赋给 Action 的那个属性值决定压到值栈栈顶的对象, 最后再为栈顶对象的属性赋值.

对于 edit 操作而言:

I.   先为 EmployeeAction 的 employeeId 赋值

II.  根据 employeeId 从数据库中加载对应的对象, 并放入到值栈的栈顶

III. 再为栈顶对象的 employeeId 赋值(实际上此时 employeeId 属性值已经存在)

IV.  把栈顶对象的属性回显在表单中.

struts.xml

        <!-- 配置使用paramPrepareParamStack -->
        <default-interceptor-ref name="paramsPrepareParamsStack"></default-interceptor-ref>
        
EmployeeAction.java

public class EmployeeAction implements RequestAware, ModelDriven<Employee> {
    private Dao dao = new Dao();
    public String list(){
        request.put("list", dao.getEmployeeList());
        return "list";
    }
    
    public String edit(){
        return "edit";
    }
    
    public String update(){
        dao.updateEmployee(employee);
        return "success";
    }
    
    public String delete(){
        dao.deleteEmployee(employeeId);
        return "success";
    }
    private Integer employeeId;

    public Integer getEmployeeId() {
        return employeeId;
    }

    public void setEmployeeId(Integer employeeId) {
        this.employeeId = employeeId;
    }

    public Dao getDao() {
        return dao;
    }

    public void setDao(Dao dao) {
        this.dao = dao;
    }
    
    private Map<String, Object> request;
    @Override
    public void setRequest(Map<String, Object> map) {
        request = map;
    }
    private Employee employee;
    @Override
    public Employee getModel() {
        if(employeeId == null){
            employee = new Employee();
        }else{
            employee = dao.getEmployee(employeeId);
        }
        return employee;
    }
}


5). 存在的问题: 

getModel 方法

public Employee getModel() {
if(employeeId == null)
<span style="white-space:pre">	</span>employee = new Employee();
else
<span style="white-space:pre">	</span>employee = dao.get(employeeId);
<span style="white-space:pre">	</span>return employee;
}

I.   在执行删除的时候, employeeId 不为 null, 但 getModel 方法却从数据库加载了一个对象. 不需加载!

II.  指向查询全部信息时, 也 new Employee() 对象. 浪费!

6). 解决方案: 使用 PrepareInterceptor 和 Preparable 接口. 

7). 关于 PrepareInterceptor

若 Action 实现了 Preparable 接口, 则 Struts 将尝试执行 prepare[ActionMethodName] 方法,

若 prepare[ActionMethodName] 不存在, 则将尝试执行 prepareDo[ActionMethodName] 方法.

若都不存在, 就都不执行.

若 PrepareInterceptor  的 alwaysInvokePrepare 属性为 false, 

则 Struts2 将不会调用实现了 Preparable 接口的  Action 的 prepare() 方法

可以为每一个 ActionMethod 准备 prepare[ActionMethdName] 方法, 而抛弃掉原来的 prepare() 方法

将 PrepareInterceptor  的 alwaysInvokePrepare 属性置为 false, 以避免 Struts2 框架再调用 prepare() 方法.


如何在配置文件中为拦截器栈的属性赋值: 参看 /struts-2.3.15.3/docs/WW/docs/interceptors.html

<interceptors>

    <interceptor-stack name="parentStack">

        <interceptor-ref name="defaultStack">

            <param name="params.excludeParams">token</param>

        </interceptor-ref>

    </interceptor-stack>

</interceptors>

 

<default-interceptor-ref name="parentStack"/>

----------------------------------源代码解析---------------------------------

public String doIntercept(ActionInvocation invocation) throws Exception {
	//获取 Action 实例
    Object action = invocation.getAction();

	//判断 Action 是否实现了 Preparable 接口
    if (action instanceof Preparable) {
        try {
            String[] prefixes;
            //根据当前拦截器的 firstCallPrepareDo(默认为 false) 属性确定 prefixes
            if (firstCallPrepareDo) {
                prefixes = new String[] {ALT_PREPARE_PREFIX, PREPARE_PREFIX};
            } else {
                prefixes = new String[] {PREPARE_PREFIX, ALT_PREPARE_PREFIX};
            }
            //若为 false, 则 prefixes: prepare, prepareDo
            //调用前缀方法.
            PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes);
        }
        catch (InvocationTargetException e) {

            Throwable cause = e.getCause();
            if (cause instanceof Exception) {
                throw (Exception) cause;
            } else if(cause instanceof Error) {
                throw (Error) cause;
            } else {
                throw e;
            }
        }

		//根据当前拦截器的 alwaysInvokePrepare(默认是 true) 决定是否调用 Action 的 prepare 方法
        if (alwaysInvokePrepare) {
            ((Preparable) action).prepare();
        }
    }

    return invocation.invoke();
}

PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes) 方法: 

public static void invokePrefixMethod(ActionInvocation actionInvocation, String[] prefixes) throws InvocationTargetException, IllegalAccessException {
	//获取 Action 实例
	Object action = actionInvocation.getAction();
	//获取要调用的 Action 方法的名字(update)
	String methodName = actionInvocation.getProxy().getMethod();
	
	if (methodName == null) {
		// if null returns (possible according to the docs), use the default execute 
        methodName = DEFAULT_INVOCATION_METHODNAME;
	}
	
	//获取前缀方法
	Method method = getPrefixedMethod(prefixes, methodName, action);
	
	//若方法不为 null, 则通过反射调用前缀方法
	if (method != null) {
		method.invoke(action, new Object[0]);
	}
}

PrefixMethodInvocationUtil.getPrefixedMethod 方法: 

public static Method getPrefixedMethod(String[] prefixes, String methodName, Object action) {
	assert(prefixes != null);
	//把方法的首字母变为大写
	String capitalizedMethodName = capitalizeMethodName(methodName);
    
    //遍历前缀数组
    for (String prefixe : prefixes) {
        //通过拼接的方式, 得到前缀方法名: 第一次 prepareUpdate, 第二次 prepareDoUpdate
        String prefixedMethodName = prefixe + capitalizedMethodName;
        try {
        	//利用反射获从 action 中获取对应的方法, 若有直接返回. 并结束循环.
            return action.getClass().getMethod(prefixedMethodName, EMPTY_CLASS_ARRAY);
        }
        catch (NoSuchMethodException e) {
            // hmm -- OK, try next prefix
            if (LOG.isDebugEnabled()) {
                LOG.debug("cannot find method [#0] in action [#1]", prefixedMethodName, action.toString());
            }
        }
    }
	return null;
}


struts.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
	"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
	"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
	<!-- 配置 Struts 可以受理的请求的扩展名 -->
	<constant name="struts.action.extension" value="action,do,"></constant>
	
	<package name="app" namespace="/" extends="struts-default">
	
		<!-- 配置使用 paramsPrepareParamsStack 作为默认的拦截器栈 -->
		<!-- 修改 PrepareInterceptor 拦截器的 alwaysInvokePrepare 属性值为 false -->
		<interceptors>
		    <interceptor-stack name="atguigustack">
		        <interceptor-ref name="paramsPrepareParamsStack">
		            <param name="prepare.alwaysInvokePrepare">false</param>
		        </interceptor-ref>
		    </interceptor-stack>
		</interceptors>
 
		<default-interceptor-ref name="atguigustack"/>
		
		<action name="emp-*" class="com.iThings.app.EmployeeAction" 
			method="{1}">
			<result name="{1}">/emp-{1}.jsp</result>
			<result name="success" type="redirectAction">emp-list</result>	
		</action>
		
	</package>
</struts>


emp-list.jsp

<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    
    <title>My JSP 'emp-list.jsp' starting page</title>
    
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
	<!--
	<link rel="stylesheet" type="text/css" href="styles.css">
	-->

  </head>
  
  <body>
	<s:form action="emp-save">
		<s:textfield label="FirstName" name="firstName"></s:textfield>
		<s:textfield label="LastName" name="lastName"></s:textfield>
		<s:textfield label="Email" name="email"></s:textfield>
		<s:submit></s:submit>
	</s:form>
	<br>
	<table cellpadding="10" cellspacing="0" border="1">
		<thead>
			<tr>
				<td>ID</td>
				<td>FirstName</td>
				<td>LastName</td>
				<td>Email</td>
				<td>Edit</td>
				<td>Delete</td>
			</tr>
		</thead>
		<tbody>
			<s:iterator value="#request.emps">
				<tr>
					<td>${employeeId }</td>
					<td>${firstName }</td>
					<td>${lastName }</td>
					<td>${email }</td>
					<td><a href="emp-edit.action?employeeId=${employeeId }">Edit</a></td>
					<td><a href="emp-delete.action?employeeId=${employeeId }">DELETE</a></td>
				</tr>
			</s:iterator>
		</tbody>
	</table>
  </body>
</html>

emp-edit.jsp

<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    
    <title>My JSP 'emp-update.jsp' starting page</title>
    
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
	<!--
	<link rel="stylesheet" type="text/css" href="styles.css">
	-->

  </head>
  
  <body>
	<s:form action="emp-update.action">
		<s:hidden name="employeeId"></s:hidden>
		<s:textfield name="firstName" label="FirstName"></s:textfield>
		<s:textfield name="lastName" label="LastName"></s:textfield>
		<s:textfield name="email" label="Email"></s:textfield>
		<s:submit></s:submit>
	</s:form>
  </body>
</html>


Employee.java

package com.iThings.entity;

public class Employee {
	private Integer employeeId;
	private String firstName;
	private String lastName;

	private String email;

	public Integer getEmployeeId() {
		return employeeId;
	}

	public void setEmployeeId(Integer employeeId) {
		this.employeeId = employeeId;
	}

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public Employee(Integer employeeId, String firstName, String lastName,
			String email) {
		super();
		this.employeeId = employeeId;
		this.firstName = firstName;
		this.lastName = lastName;
		this.email = email;
	}

	public Employee() {
		// TODO Auto-generated constructor stub
	}

	@Override
	public String toString() {
		return "Employee [employeeId=" + employeeId + ", firstName="
				+ firstName + ", lastName=" + lastName + ", email=" + email
				+ "]";
	}
	
}


EmployeeDao.java

package com.iThings.dao;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.List;

import com.iThings.entity.Employee;

public class EmployeeDao {
	private static Map<Integer,Employee> emps = new LinkedHashMap<Integer, Employee>();
	static{
		emps.put(1001, new Employee(1001, "AA", "aa", "aa@163.com"));
		emps.put(1002, new Employee(1002, "BB", "bb", "bb@163.com"));
		emps.put(1003, new Employee(1003, "CC", "cc", "cc@163.com"));
		emps.put(1004, new Employee(1004, "DD", "dd", "dd@163.com"));
		emps.put(1005, new Employee(1005, "EE", "ee", "ee@163.com"));
	}
	//查询--批量
	public List<Employee> getEmployeeList(){
		return new ArrayList<Employee>(emps.values());
	}
	//查询--单个
	public Employee getEmployeeById(Integer empId){
		return emps.get(empId);
	}
	//保存
	public void saveEmployee(Employee employee){
		//保存时生成ID
		long empId = System.currentTimeMillis();
		employee.setEmployeeId((int) empId);
		emps.put(employee.getEmployeeId(), employee);
	}
	//删除
	public void delEmployee(Integer empId){
		emps.remove(empId);
	}
	//修改
	public void updateEmployee(Employee employee){
		emps.put(employee.getEmployeeId(), employee);
	}
}


EmployeeAction.java

package com.iThings.app;

import java.util.List;
import java.util.Map;

import org.apache.struts2.interceptor.RequestAware;

import com.iThings.dao.EmployeeDao;
import com.iThings.entity.Employee;
import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.Preparable;

public class EmployeeAction implements RequestAware, ModelDriven<Employee>, Preparable {
	
	private Employee employee;
	
	private Integer employeeId;
	
	public void setEmployeeId(Integer employeeId) {
		this.employeeId = employeeId;
	}
	
	
	private EmployeeDao employeeDao = new EmployeeDao();
	
	private Map<String,Object> request;
	
	@Override
	public void setRequest(Map<String, Object> arg0) {
		// TODO Auto-generated method stub
		this.request = arg0;
	}
	
	public String list(){
		List<Employee> employeeList = employeeDao.getEmployeeList();
		request.put("emps", employeeList);
		return "list";
	}
	
	public String save(){
		employeeDao.saveEmployee(employee);
		return "success";
	}
	public void prepareSave(){
		employee = new Employee();
	}
	
	public String delete(){
		employeeDao.delEmployee(employeeId);
		return "success";
	}
	
	public String edit(){
		return "edit";
	}
	
	public void prepareEdit(){
		employee = employeeDao.getEmployeeById(employeeId);
	}
	
	public String update(){
		employeeDao.updateEmployee(employee);
		return "success";
	}
	
	public void prepareUpdate(){
		employee = new Employee();
	}
	
	@Override
	public Employee getModel() {
		return employee;
	}

	@Override
	public void prepare() throws Exception {
		// TODO Auto-generated method stub
		
	}

}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值