Strut2(主题 ModelDriven 和 Preparable 拦截器)

介绍

主题: 为了让所有的 UI 标签能够产生同样的视觉效果而归集到一起的一组模板. 即风格相近的模板被打包为一个主题
simple: 把 UI 标签翻译成最简单的 HTML 对应元素, 而且会忽视行标属性
xhtml: xhtml 是默认的主题. 这个主题的模板通过使用一个布局表格提供了一种自动化的排版机制.
css_xhtml: 这个主题里的模板与 xhtml 主题里的模板很相似, 但它们将使用 css 来进行布局和排版
ajax: 这个主题里的模板以 xhtml 主题里德模板为基础, 但增加了一些 Ajax 功能.
修改主题:
通过 UI 标签的 theme 属性
在一个表单里, 若没有给出某个 UI 标签的 theme 属性, 它将使用这个表单的主题
在 page, request, session 或 application 中添加一个 theme 属性
修改 struts.properties 文件中的 struts.ui.theme 属性.

例如:

	<% request.setAttribute("theme","simple"); %>
		<s:form action="save" theme="simple">
<constant name="struts.ui.theme" value="simple"></constant>

ModelDriven 和 Preparable 拦截器

Struts2 运行流程图-1

在这里插入图片描述

Params 拦截器

Parameters 拦截器将把表单字段映射到 ValueStack 栈的栈顶对象的各个属性中. 如果某个字段在模型里没有匹配的属性, Param 拦截器将尝试 ValueStack 栈中的下一个对象
在这里插入图片描述

把 Action 和 Model 隔开

在使用 Struts 作为前端的企业级应用程序时把 Action 和 Model 清晰地隔离开是有必要的: 有些 Action 类不代表任何Model 对象, 它们的功能仅限于提供显示服务
在这里插入图片描述

ModelDriven 拦截器

当用户触发 add 请求时, ModelDriven 拦截器将调用 EmployeeAction 对象的 getModel() 方法, 并把返回的模型(Employee实例)压入到 ValueStack 栈.
接下来 Parameters 拦截器将把表单字段映射到 ValueStack 栈的栈顶对象的各个属性中. 因为此时 ValueStack 栈的栈顶元素是刚被压入的模型(Employee)对象, 所以该模型将被填充. 如果某个字段在模型里没有匹配的属性, Param 拦截器将尝试 ValueStack 栈中的下一个对象
在这里插入图片描述
例子:
emp-list.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="emp-save">
		
		<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>

	<br>
	<hr>
	<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?employeeId=${employeeId }">Edit</a></td>
					<td><a href="emp-delete?employeeId=${employeeId }">Delete</a></td>
				</tr>
			</s:iterator>
		</tbody>
		
	</table>

</body>
</html>

<%
	System.out.println("now: " + new java.util.Date());
%>

emp-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:debug></s:debug>
	
	<br>
	<br>
	
	<s:form action="emp-update">
		
		<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 product;

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
				+ "]";
	}
	
	

}

Dao.java

package product;

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

public class Dao {
	
	private static Map<Integer, Employee> emps = new LinkedHashMap<Integer, Employee>();
	
	static{
		emps.put(1001, new Employee(1001, "AA", "aa", "[email protected]"));
		emps.put(1002, new Employee(1002, "BB", "bb", "[email protected]"));
		emps.put(1003, new Employee(1003, "CC", "cc", "[email protected]"));
		emps.put(1004, new Employee(1004, "DD", "dd", "[email protected]"));
		emps.put(1005, new Employee(1005, "EE", "ee", "[email protected]"));
	}
	
	public List<Employee> getEmployees(){
		return new ArrayList<Employee>(emps.values());
	}
	
	public void delete(Integer empId){
		emps.remove(empId);
	}
	
	public void save(Employee emp){
		long time = System.currentTimeMillis();
		emp.setEmployeeId((int)time);
		
		emps.put(emp.getEmployeeId(), emp);
	}
	
	public Employee get(Integer empId){
		return emps.get(empId);
	}
	
	public void update(Employee emp){
		emps.put(emp.getEmployeeId(), emp);
	}
	
}

EmployeeAction.java

package product;

import java.util.Map;

import org.apache.struts2.interceptor.RequestAware;

import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.Preparable;

public class EmployeeAction implements RequestAware, ModelDriven<Employee>{
	
	private Dao dao = new Dao();
	
	private Employee employee;
	
	public String update(){
		dao.update(employee);
		return "success";
	}
	
//	public void prepareUpdate(){
//		employee = new Employee();
//	}

	public String edit(){
		Employee e=dao.get(employee.getEmployeeId());
		employee.setEmail(e.getEmail());
		employee.setFirstName(e.getFirstName());
		employee.setLastName(e.getLastName());
		return "edit";
	}
	
//	public void prepareEdit(){
//		employee = dao.get(employeeId);
//	}
	
	public String save(){
		dao.save(employee);
		return "success";
	}
	
//	public void prepareSave(){
//		employee = new Employee();
//	}
	
	public String delete(){
		dao.delete(employeeId);
		return "success";
	}
	
	public String list(){
		request.put("emps", dao.getEmployees());
		return "list";
	}
	
	private Map<String, Object> request;

	@Override
	public void setRequest(Map<String, Object> arg0) {
		this.request = arg0;
	}
	
	private Integer employeeId;
	
	public void setEmployeeId(Integer employeeId) {
		this.employeeId = employeeId;
	}
	
	@Override
	public Employee getModel() {
		employee = new Employee();
		return employee;
	}

//	@Override
//	public void prepare() throws Exception {
//		System.out.println("prepare...");
//	}
	
}

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>
<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
<constant name="struts.action.extension" value="action,do,"></constant>
<constant name="struts.ui.theme" value="simple"></constant>
<package name="hello" extends="struts-default" >


		<action name="emp-*" 
			class="product.EmployeeAction"
			method="{1}">
			<result name="{1}">/emp-{1}.jsp</result>
			<result name="success" type="redirectAction">emp-list</result>	
		</action>
		
		<action name="emp-list"
			class="product.EmployeeAction"
			method="list">
			<result name="list">/emp-list.jsp</result>	
		</action>
</package>

</struts>

简化方法

注意(此法错误)
EmployeeAction.java
在这里插入图片描述
方法二:EmployeeAction.java edit方法改成

ActionContext.getContext().getValueStack().push(dao.get(employee.getEmployeeId()));

方法三:
Struts.xml
package中换个默认拦截器栈(1). paramsPrepareParamsStack 和 defaultStack 一样都是拦截器栈. 而 struts-default 包默认使用的是
defaultStack)

paramsPrepareParamsStack 拦截器在于
params -> modelDriven -> params
所以可以先把请求参数赋给 Action 对应的属性, 再根据赋给 Action 的那个属性值决定压到值栈栈顶的对象, 最后再为栈顶对象的属性赋值.

对于 本例子edit 操作而言:
I. 先为 EmployeeAction 的 employeeId 赋值
II. 根据 employeeId 从数据库中加载对应的对象, 并放入到值栈的栈顶
III. 再为栈顶对象的 employeeId 赋值(实际上此时 employeeId 属性值已经存在)
IV. 把栈顶对象的属性回显在表单中.

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

EmployeeAction.java改善

package product;

import java.util.Map;

import org.apache.struts2.interceptor.RequestAware;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.Preparable;

public class EmployeeAction implements RequestAware, ModelDriven<Employee>{
	
	private Dao dao = new Dao();
	
	private Employee employee;
	
	public String update(){
		dao.update(employee);
		return "success";
	}
	
//	public void prepareUpdate(){
//		employee = new Employee();
//	}

	public String edit(){
//		Employee e=dao.get(employee.getEmployeeId());
//		employee.setEmail(e.getEmail());
//		employee.setFirstName(e.getFirstName());
//		employee.setLastName(e.getLastName());
//		
		return "edit";
	}
	
	public void prepareEdit(){
		employee = dao.get(employeeId);
	}
	
	public String save(){
		dao.save(employee);
		return "success";
	}
	
//	public void prepareSave(){
//		employee = new Employee();
//	}
	
	public String delete(){
		dao.delete(employeeId);
		return "success";
	}
	
	public String list(){
		request.put("emps", dao.getEmployees());
		return "list";
	}
	
	private Map<String, Object> request;

	@Override
	public void setRequest(Map<String, Object> arg0) {
		this.request = arg0;
	}
	
	private Integer employeeId;
	
	public void setEmployeeId(Integer employeeId) {
		this.employeeId = employeeId;
	}
	
	@Override
	public Employee getModel() {
	
1/判断是Create还是Edit.

//若为Create,则employee = new Employee();

//若为Edit,则employee = dao. get (employeeId);

//判定标准为是否有employeeId这个请求参数。若有该参数。则视为Edit;若没有该多数。则视为Create//若通过employeeId来判断,则需要在modelDriven拦截器之前先执行一个params拦截器!

//而这可以通过使用paransPrepareParams拦截器栈实现。

//害要在struts.xml文件中配置使用paramsPrepareParams作为默认的拦截器栈。
		if(employeeId==null)
		employee = new Employee();
		else
			employee=dao.get(employeeId);	
		return employee;
	}

//	@Override
//	public void prepare() throws Exception {
//		System.out.println("prepare...");
//	}
	
}

相关拦截器

在这里插入图片描述
方法三存在的问题
I. 在执行删除的时候, employeeId 不为 null, 但 getModel 方法却从数据库加载了一个对象. 不该加载!
II. 指向查询全部信息时, 也 new Employee() 对象. 浪费!
6). 解决方案: 使用 PrepareInterceptor 和 Preparable 接口.

Preparable 拦截器

Struts 2.0 中的 modelDriven 拦截器负责把 Action 类以外的一个对象压入到值栈栈顶
prepare 拦截器负责准备为 getModel() 方法准备 model

若 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() 方法.
改善代码
在前面代码的基础上
EmployeeAction.java改善

import java.util.Map;

import org.apache.struts2.interceptor.RequestAware;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.Preparable;

public class EmployeeAction implements RequestAware, ModelDriven<Employee>,Preparable{
	
private Dao dao = new Dao();
	
	private Employee employee;
	
	public String update(){
		dao.update(employee);
		return "success";
	}
	
	public void prepareUpdate(){
		employee = new Employee();
	}

	public String edit(){	
		return "edit";
	}
	
	public void prepareEdit(){
		employee = dao.get(employeeId);
	}
	
	public String save(){
		dao.save(employee);
		return "success";
	}
	
	public void prepareSave(){
		employee = new Employee();
	}
	
	public String delete(){
		dao.delete(employeeId);
		return "success";
	}
	
	public String list(){
		request.put("emps", dao.getEmployees());
		return "list";
	}
	
	private Map<String, Object> request;

	@Override
	public void setRequest(Map<String, Object> arg0) {
		this.request = arg0;
	}
	
	private Integer employeeId;
	
	public void setEmployeeId(Integer employeeId) {
		this.employeeId = employeeId;
	}
	
	@Override
	public Employee getModel() {
		return employee;
	}

	@Override
	public void prepare() throws Exception {
		System.out.println("prepare...");
	}
	
}

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>
<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
<constant name="struts.action.extension" value="action,do,"></constant>
<constant name="struts.ui.theme" value="simple"></constant>
<package name="hello" extends="struts-default" >
	<!-- 修改 PrepareInterceptor 拦截器的 alwaysInvokePrepare 属性值为 false -->
		<interceptors>
		    <interceptor-stack name="at">
		        <interceptor-ref name="paramsPrepareParamsStack">
		            <param name="prepare.alwaysInvokePrepare">false</param>
		        </interceptor-ref>
		    </interceptor-stack>
		</interceptors>
 
		<default-interceptor-ref name="at"/>

		<action name="emp-*" 
			class="product.EmployeeAction"
			method="{1}">
			<result name="{1}">/emp-{1}.jsp</result>
			<result name="success" type="redirectAction">emp-list</result>	
		</action>
		
		<action name="emp-list"
			class="product.EmployeeAction"
			method="list">
			<result name="list">/emp-list.jsp</result>	
		</action>
</package>

</struts>

PrepareInterceptor总结

若 Action 实现 Preparable 接口,则 Action 方法需实现 prepare() 方法
PrepareInterceptor 拦截器将调用 prepare() 方法,prepareActionMethodName()方法 或 prepareDoActionMethodName ()方法
PrepareInterceptor 拦截器根据 firstCallPrepareDo 属性决定获取 prepareActionMethodName 、prepareDoActionMethodName的顺序。默认情况下先获取 prepareActionMethodName (), 如果没有该方法,就寻找prepareDoActionMethodName()。如果找到对应的方法就调用该方法
PrepareInterceptor 拦截器会根据 alwaysInvokePrepare 属性决定是否执行prepare()方法

使用 paramsPrepareParamsStack 拦截器栈

paramsPrepareParamsStack 从字面上理解来说, 这个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的业务逻辑执行
    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/feiqipengcheng/article/details/106534246