2.为什么使用ModelDriven拦截器?
①在我们struts2的企业级开发中,把Action和Model清晰的隔离开是很有必要的。有些Action不代表任何Model对象,它们的功能仅限于显示服务。把动作对象和model模型隔离开说明我们的模型对象不能再是动作对象的实例,那么我们如何让提交的参数映射到model对象里面去呢?那就要使用modeldriven拦截器。
②实现了modeldriven拦截器的Action类需要实现getModel()方法。当用户触发动作时,ModelDriven会调用相应Action动作对象的getModel()方法,并且把getModel()方法返回的model对象压入到值栈中的Object stack的栈顶。那么Parameter拦截器将会将页面提交的参数映射到栈顶的模型对象相应的属性当中去,如果某个参数模型对象中没有与之匹配的属性,那么将从栈顶的下一个对象中继续找与之匹配的属性并且映射到相应的属性中去。
3.那么为什么需要使用Preparable拦截器呢?
①struts2的ModelDriven的getModel()方法返回一个model对象,ModelDriven将该对象压入到栈顶中去,而这里的Preparable拦截器则负责为我们的getModel()方法准备model对象。
②实现了Preparable接口的Action类需要实现prepare()方法。但是在执行prepare()方法之前,struts尝试执行prepare[ActionMethodName]()方法,若prepare[ActionMethodName]()方法不存在,那么尝试执行下一个prepareDo[ActionMethodName]方法,如果也不存在就都不执行。最后再执行prepare()方法。这里需要说明的是,加入我们的Action中有一个处理action请求的add()方法,在Action类中添加prepareAdd()或者prepareDoAdd()方法,那么就可以通过该方法来为当前的Action准备模型对象并压入到栈顶,是的传过来的参数成功映射到model对象中去。
③我们可以通过设置PrepareInterceptor拦截器的alwaysInvokePrepare属性来告诉struts是否执行prepare()方法。默认是执行prepare()方法的。如果想要设置不执行prepare()方法,方法如下:
在struts.xml配置文件中配置
<interceptors>
<interceptor-stack name="parentStack">
<interceptor-ref name="拦截器栈名">
<param name="prepare.alwaysInvokePrepare">false</param>
</interceptor-ref>
</interceptor-stack>
</interceptors>
4.paramsPrepareParamsStack栈有什么作用呢?
①paramsPrepareParamsStack是用来设置action请求时执行拦截器的循序。从字面上理解来说,这个stack的拦截器调用拦截器的顺序是为如下:
<interceptor-stack name="paramsPrepareParamsStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="datetime"/>
<interceptor-ref name="multiselect"/>
<!--往栈顶对象中填充参数 此时栈顶对象时Action类-->
<interceptor-ref name="params"/>
<interceptor-ref name="servletConfig"/>
<!--准备model对象-->
<interceptor-ref name="prepare"/>
<interceptor-ref name="chain"/>
<!--将model对象压入到object stack的栈顶-->
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="actionMappingParams"/>
<!--往栈顶对象填充参数 此时栈顶对象是model对象-->
<interceptor-ref name="params"/>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
</interceptor-stack>
②struts2设计时将modeldriven拦截器放到了params拦截器之前。这里参数就能成功的设置到栈顶的model对象当中去,但是在prepare拦截器准备model对象时有需要某些提交过来的参数,那么就需要在执行modeldriven之前再执行一次params拦截器,这就是设置拦截器栈paramsPrepareParamsStack的原因。
③根据paramsPrepareParamsStack的拦截器顺序我们可以看出其重要的几个流程如下:
1)当发出action请求时,struts2创建一个对应的Action类放到栈顶,第一个params拦截器将特定的属性映射到Action对象中去。
2)prepare拦截器根据Action中的某些属性来准备model对象。
3)modelDriven拦截器将准备好的model对象压入到值栈的栈顶。
4)params拦截器再将参数映射到栈顶的model对象中去。
5)执行Action类中的action请求对象的方法。
最后根据学习到的几个拦截器写了一个小的案例,代码如下:
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>
<package name="default" extends="struts-default" namespace="/crud">
<interceptors>
<interceptor-stack name="parentStack">
<interceptor-ref name="paramsPrepareParamsStack">
<param name="prepare.alwaysInvokePrepare">false</param>
</interceptor-ref>
</interceptor-stack>
</interceptors>
<!-- 设置默认的拦截器栈 设置为参数准备拦截器栈 在放入model之前先把参数给栈顶的Action对象 -->
<default-interceptor-ref name="parentStack"></default-interceptor-ref>
<action name="employee-show" class="com.yd.action.EmployeeAction"
method="show">
<result name="show">/show.jsp</result>
</action>
<action name="employee-*" class="com.yd.action.EmployeeAction"
method="{1}">
<result name="{1}" type="redirectAction">
<param name="namespace">/crud</param>
<param name="actionName">employee-show</param>
</result>
</action>
<action name="employee-edit" class="com.yd.action.EmployeeAction"
method="edit">
<result name="edit">/edit.jsp</result>
</action>
</package>
</struts>
index.jsp页面如下:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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">
<title>Insert title here</title>
</head>
<body>
<h1><a href="/Struts2_7/crud/employee-show.action">ShowEmployees</a></h1>
</body>
</html>
show.jsp页面如下:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags" %>
<!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">
<title>Insert title here</title>
</head>
<body>
<s:form action="employee-add.action" method="post" namespace="/crud">
<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 value="submit"></s:submit>
</s:form>
<hr/>
<table align="center" cellpadding="0" cellspacing="0" border="1" style="width:500px;border-collapse:collapse">
<thead align="center">
<td>EmployeeId</td>
<td>FirstName</td>
<td>LastName</td>
<td>Email</td>
</thead>
<s:iterator value="[0].getEmployees()">
<tr>
<td>${employeeId }</td>
<td>${firstName }</td>
<td>${lastName }</td>
<td>${email }</td>
<td><a href="/Struts2_7/crud/employee-edit.action?employeeId=${employeeId}">编辑</a></td>
<td><a href="/Struts2_7/crud/employee-delete.action?employeeId=${employeeId}">删除</a></td>
</tr>
</s:iterator>
</table>
<s:debug></s:debug>
</body>
</html>
edit.jsp页面如下:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags" %>
<!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">
<title>Insert title here</title>
</head>
<body>
<s:form action="employee-update.action" method="post" namespace="/crud">
<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 value="submit"></s:submit>
</s:form>
<s:debug></s:debug>
</body>
</html>
EmployeeDao类用于模拟数据库查询类如下:
package com.yd.dao;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.yd.bean.Employee;
public class EmployeeDao {
private static Map<Integer, Employee> employees=new LinkedHashMap<>();
static{
//静态块在类加载的时候执行
employees.put(1001, new Employee(1001,"aa","AA","aa@qq.com"));
employees.put(1002, new Employee(1002,"bb","BB","bb@qq.com"));
employees.put(1003, new Employee(1003,"cc","CC","cc@qq.com"));
employees.put(1004, new Employee(1004,"dd","DD","dd@qq.com"));
}
//获得员工链表
public static List<Employee> getEmployees(){
return new ArrayList<>(employees.values());
}
//添加一个员工
public static void addEmployee(Employee employee){
employee.setEmployeeId(Integer.parseInt((new Date().getTime()+"").substring(4)));
employees.put(employee.getEmployeeId(), employee);
}
//删除一个员工
public static void deleteEmployee(Integer employeeId){
employees.remove(employeeId);
}
//获得一个员工的信息
public static Employee getEmployee(Integer employeeId){
return employees.get(employeeId);
}
//修改一个员工的信息
public static void updateEmployee(Employee employee){
employees.put(employee.getEmployeeId(), employee);
}
}
Employee类(model类)如下:
package com.yd.bean;
public class Employee {
private Integer employeeId;
private String firstName;
private String lastName;
private String 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() {
super();
}
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;
}
@Override
public String toString() {
return "Employee [employeeId=" + employeeId + ", firstName=" + firstName + ", lastName=" + lastName + ", email="
+ email + "]";
}
}
Action处理类EmployeeAction类如下:
package com.yd.action;
import java.util.List;
import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.Preparable;
import com.yd.bean.Employee;
import com.yd.dao.EmployeeDao;
public class EmployeeAction implements ModelDriven<Employee>,Preparable{
private List<Employee> employees=null;
private Employee employee=null;
private Integer employeeId;
public Integer getEmployeeId() {
return employeeId;
}
public void setEmployeeId(Integer employeeId) {
this.employeeId = employeeId;
}
public List<Employee> getEmployees() {
return EmployeeDao.getEmployees();
}
public void setEmployees(List<Employee> employees) {
this.employees = employees;
}
//显示员工的链表
public String show(){
return "show";
}
//删除一个员工的信息
public String delete(){
EmployeeDao.deleteEmployee(employeeId);
return "delete";
}
//添加一个员工
public String add(){
EmployeeDao.addEmployee(employee);
return "add";
}
public void prepareAdd(){
employee=new Employee();
}
//编辑一个员工
public String edit(){
return "edit";
}
public void prepareEdit(){
employee=EmployeeDao.getEmployee(employeeId);
}
//更新一个员工信息
public String update(){
EmployeeDao.updateEmployee(employee);
return "update";
}
public void prepareUpdate(){
employee=EmployeeDao.getEmployee(employeeId);
}
//将返回的对象压入到值栈栈顶
@Override
public Employee getModel() {
return employee; //如果return 是个null也不会放入到值栈中去
}
//在返回之前执行的方法,用于准备model对象的
@Override
public void prepare() throws Exception {
System.out.println("我在执行一个prepare");
}
}