Struts2_CRUD操作實例

Struts2_CRUD操作實例

Struts2運行流程:


瀏覽器先發了個請求,
先會到StrutsPrepareAndExecuteFilter的doFilter方法,
然後創建了一個StrutsActionProxy(代理),調用了這個代理的execute方法,
StrutsActionProxy裏有一個DefaultActionInvocation的引用,調用了DefaultActionInvocation裏的invoke()方法,
接着DefaultActionInvocation去調攔截器的intercept()方法,
攔截器ExceptionMappingInterceptor接着又回調DefaultActionInvocation的invoke()方法,
DefaultActionInvocation再調下一個攔截器的intercept()方法,然後再回調,
如此往復嗎,到調完最後一個攔截器再回調後,將調用自己的invokeAction(),
最終調用Action的目標方法。

1.ActionProxy:是Action的一個代理類,也就是說Action的調用是通過ActionProxy實現的,其實就是調用了ActionProxy.execute方法,而該方法又調用了ActionInvocation.invoke()方法。負責調用目標Action方法之前的攔截器的調用。
2.ActionInvocation:就是Action的調用者。ActionInvocation在Action的執行過程中,負責Interceptor,Action和Result等一系列元素的調度。


Params攔截器
Parameters攔截器將把表單字段映射到ValueStack棧的棧頂對象的各個屬性中,如果某個字段在模型裏沒有匹配的屬性,Param攔截器將嘗試ValueStack棧中的下一個對象。
默認情況下,棧頂對象就是Action。

把Action和Model隔開
在使用Struts作爲前端的企業級應用程序時把Action和Model清晰的隔離開是有必要的,有些Action類不代表任何Model對象,他們的功能僅限於提供顯示服務。

 

如果Action類實現了ModelDriven接口,該攔截器將把ModelDriven接口的getModel()方法返回的對象置於棧頂

 

1.Action實現ModelDriven接口後的運行流程
1).先會執行ModelDrivenInterceptor的interceptor方法
 public String intercept(ActionInvocation invocation) throws Exception {
      //獲取Action對象:EmployeeAction對象,此時該Action已經實現了ModelDriven接	口
        Object action = invocation.getAction();

     //判斷action是否是ModelDriven的實例
        if (action instanceof ModelDriven) {
          //強制轉換爲ModelDriven類型
            ModelDriven modelDriven = (ModelDriven) action;
          //獲取值棧
            ValueStack stack = invocation.getStack();
          //調用ModelDriven接口的getModel()方法
          //即調用EmployeeAction的getModel()方法
            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方法
            public Employee getModel() {
	// TODO 自動生成的方法存根
	        employee = new  Employee();	
	         return employee;
	}
    不能寫成return new  Employee();,這樣與成員變量employee 就沒有了聯繫,當前Action的employee成員變量是null;如下圖:



ModelDriven攔截器
當用戶觸發add請求時,ModelDriven攔截器將調用EmployeeAction對象的getModel()方法,並把返回的模型(Employee實例)壓入到ValueStack棧。
接下來Parameters攔截器將把表單字段映射到ValueStack棧的棧頂對象的各個屬性中,因爲此時ValueStack棧的棧頂元素則是剛被壓入的模型(Employee)對象,所以該模型將被填充。如果某個字段在模型裏沒有匹配的屬性,Param攔截器將嘗試ValueStack棧中的下一個對象。

回顯:
1.客戶端發送請求 employee.edit
2.getModel()把employee對象置於棧頂,
  此時棧頂對象爲employee,所以把請求參數賦給employee的對應屬性
3.public String edit()方法
  1. 從數據庫中獲取了employeeId對應的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.把棧頂對象的屬性回顯在表單中。

4.關於回顯:struts2表單標籤會從值棧中獲取對應的屬性進行回顯
該實例中的問題:
I.在執行刪除的時候,employeeId不爲空,但getModel()方法(employee = dao.get(employeeId);)卻從數據庫中加載了一個對象,沒有必要。
II.在執行list()時,會employee = new  Employee();  new了個 Employee()對象,沒有必要。


解決方案:使用ParamPrepareInterceptor 的 intercept方法

關於PrepareInterceptor,源代碼解析:

 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方法:
public static void invokePrefixMethod(ActionInvocation actionInvocation, String[] prefixes) throws InvocationTargetException, IllegalAccessException {
	             //獲取Action實例
		Object action = actionInvocation.getAction();
	             //獲取要調用的Action方法的名字(update)
		String methodName = actionInvocation.getProxy().getMethod();
	           //如果方法是空就調用execute
		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);

		//若方法不爲空,則通過反射調用前綴方法
		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;
	}

分析後得到的結論:
Action實現了Preparable接口,則struts將會嘗試執行prepare[ActionMethodName]方法,
prepare[ActionMethodName]不存在,則將嘗試執行prepareDo[ActionMethodName]方法,
若都不存在,就都不執行。

ParamPrepareInterceptor alwaysInvokePrepare屬性爲false,則Struts2將不會調用實現了Preparable接口的Actionprepare()方法

解決方案:
爲每一個ActionMethod準備prepare[ActionMethodName]方法,而拋棄掉原來的prepare()方法
ParamPrepareInterceptor alwaysInvokePrepare屬性置爲false,以避免Struts2框架再調用prepare()方法


如何在 配置文件中把攔截器棧的屬性賦值:參看文檔


使用paramsPrepareParamsStack
paramsPrepareStack
從字面上理解來說,這個stack的攔截器調用的順序爲:首先params,然後prepare,接下來modelDriven,最後params
struts2
的設計上要求modelDrivenparams之前調用,而業務中prepare要負責準備model,準備model又需要參數,這就需要在prepare之前運行params攔截器設置相關參數,這個也就是創建paramsPrepareParamsStack的原因。
流程如下:
1.params
攔截器首先給action中的相關參數賦值,如id
2.prepare
攔截器執行prepare方法,prepare方法會根據參數,如id,去調用業務邏輯,設置model對象

3.modelDriven攔截器將model對象壓入ValueStack,這裏的model對象就是在prepare中創建的
4.params攔截器再次將參數賦值給model對象
5.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="wul" namespace="/" extends="struts-default">
    	
 
    	<!--配置使用paramsPrepareParamsStack作爲默認的攔截器棧  
    	<default-interceptor-ref name="paramsPrepareParamsStack"></default-interceptor-ref>
		-->
		
		<!--修改ParamPrepareInterceptor中alwaysInvokePrepare的屬性值爲false -->
		<interceptors>
			<interceptor-stack name="wulstack">
				<interceptor-ref name="paramsPrepareParamsStack">
					<param name="prepare.alwaysInvokePrepare">false</param>
				</interceptor-ref>
			</interceptor-stack>
		</interceptors>
		
		<default-interceptor-ref name="wulstack"></default-interceptor-ref>
		
		<action name="emp-*" class="com.wul.app.EmployeeAction"
			method="{1}">
			<result name="{1}">/emp-{1}.jsp</result>
			<result name="success" type="redirectAction">emp-list</result>
			
		</action>
			
    </package>

</struts>
注意:
          <interceptors>
<interceptor-stack name="wulstack">
<interceptor-ref name="paramsPrepareParamsStack">
<param name="prepare.alwaysInvokePrepare">false</param>
</interceptor-ref>
</interceptor-stack>
</interceptors>
要在<default-interceptor-ref name="wulstack"></default-interceptor-ref>前

index.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>
	
	<a href="emp-list.action">List All Employees</a>
	
</body>
</html>
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>
	
	
	<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>
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>
		
	<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 com.wul.app;

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() {
		super();
	}

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

Dao.java
package com.wul.app;

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> getEmployee(){
		return new ArrayList<>(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 com.wul.app;

import java.sql.PreparedStatement;
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 save(){
		dao.save(employee);
		return "success";
	}
	
	public void prepareSave(){
		employee = new Employee();
	}
	
	public String delete(){
		dao.delete(employeeId);
		return "success";
	}
	
	public void prepareEdit(){
		employee = dao.get(employeeId);
	}
	
	public String edit(){
		//1.獲取傳入的employeeId:employee.getEmployeeId()
		
		//2.根據employee獲取Employee對象
//		Employee emp = dao.get(employee.getEmployeeId());
		//3.把棧頂對象的屬性裝配好
		//目前的employee對象只有employeeId屬性,其他屬性爲null
		/*
		Struts2表單回顯時:從值棧棧頂開始查找匹配的屬性,若找到就添加
		到value屬性中。
		*/
//		employee.setEmail(emp.getEmail());
//		employee.setFirstName(emp.getFirstName());
//		employee.setLastName(emp.getLastName());
	//	employee = dao.get(employee.getEmployeeId());不行,經過重寫賦值的employee對象已經不再是棧頂對象了
		//手動的把從數據庫中獲取的Employee對象放到值棧的棧頂
		//但此時值棧棧頂及第二個對象均爲Employee對象,有浪費
//		ActionContext.getContext().getValueStack().push(dao.get(employee.getEmployeeId()));
		
		return "edit";
	}
	
	public void prepareUpdate(){
		employee = new Employee();
	}
	
	public String update(){
		dao.update(employee);
		return "success";
	}
	
//	//需要在當前的Employee中定義employeeId屬性。
//	//以接受請求參數
//	private Integer employeeId;
//	
//	public void setEmployeeId(Integer employeeId) {
//		this.employeeId = employeeId;
//	}
//	
//	public String delete(){
//		dao.delete(employeeId);
//		//返回結果的類型應爲:redirectAction
//		//也可以是chain:實際上chain是沒有必要的,因爲不需要在下一個Action中
//		//保留當前Action的狀態
//		//還有,若使用chain,則達到目標頁面後,地址欄顯示的依然是刪除的那個連接,刷屏時會有重複提交
//		
//		return "success";
//	}
//	
	public String list(){
		
		request.put("emps", dao.getEmployee());
		
		return "list";
	}
//	
//	private String firstName;
//	private String lastName;
//	private String email;
//	
//	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 String save(){
//		//1.獲取請求參數:通過定義對應屬性的方式
//		Employee employee = new Employee(null,firstName,lastName,email);
//		//2.調用Dao的save方法
//		dao.save(employee);
//		
//		//3.通過redirectAction的方式反應結果給emp-list	
//		return "success";
//	}
//	
	private Map<String,Object> request = null;

	@Override
	public void setRequest(Map<String, Object> arg0) {
		// TODO 自動生成的方法存根
		this.request = arg0; 
	}
	
	private Integer employeeId;
	
	public void setEmployeeId(Integer employeeId) {
		this.employeeId = employeeId;
	}
	
	@Override
	public Employee getModel() {
		// TODO 自動生成的方法存根
		//判讀是Create還是Edit。
		//若爲Create,則employee = new Employee();
		//若爲Edit,則employee = dao.get(employeeId);
		//判定標準爲是否有employeeId這個請求參數,若有該參數,則視爲Edit,若沒有該參數,則視爲Create
		//若通過employeeId來判斷,則需要在ModelDriven攔截器之前先執行一個params攔截器
		//而這可以通過使用paramsPrepareParams攔截器棧來實現
		//需要在struts.xml文件中配置使用paramsPrepareParams作爲默認的攔截器棧。
//		if(employeeId==null)
//	    employee = new  Employee();
//		else
//		employee = dao.get(employeeId);
		return employee;
	}

	//prepare方法的主要作用:爲getModel()方法準備model的。
	@Override
	public void prepare() throws Exception {
//		// TODO 自動生成的方法存根
//		if(employeeId==null)
//		    employee = new  Employee();
//		else
//			employee = dao.get(employeeId);
		System.out.println("prepare...");
	}
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>struts2_2</display-name>
  
  
   <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
 
</web-app>



















`
發佈了1 篇原創文章 · 獲贊 7 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章