1:主頁面
<a href="emp-list">emp-list</a> <br>
然后到struts.xml文件中找到對應的emp-list
<action name="emp-list" class="com.emp.app.EmployeeAction" method="List">
<result name="list">/pages/emp-list.jsp</result>
</action>
然后在Action處理業務層處理。class="com.emp.app.EmployeeAction"
package com.emp.app; import java.util.Map; import org.apache.struts2.interceptor.RequestAware; public class EmployeeAction implements RequestAware{ private Dao dao = new Dao(); private Employee employee;
private Map<String, Object> request;
public void setRequest(Map<String, Object> arg0) { this.request = arg0; } public String List(){ System.out.println(dao.getEmployees()); request.put("emps", dao.getEmployees()); System.out.println(request.size()); return "list"; }
private Integer employeeId;
public void setEmployeeId(Integer employeeId) {
this.employeeId = employeeId;
}
public String delete(){
dao.deleteEmployees(employeeId);
return "success";
} }
到DAO層處理數據:
package com.emp.app; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.util.ValueStack; public class Dao { ValueStack vs=ActionContext.getContext().getValueStack(); private static Map<Integer, Employee> emps = new LinkedHashMap<Integer, Employee>(); static{ emps.put(1001, new Employee(1001, "AA", "aa", "aa@atguigu.com")); emps.put(1002, new Employee(1002, "BB", "bb", "bb@atguigu.com")); emps.put(1003, new Employee(1003, "CC", "cc", "cc@atguigu.com")); emps.put(1004, new Employee(1004, "DD", "dd", "dd@atguigu.com")); emps.put(1005, new Employee(1005, "EE", "ee", "ee@atguigu.com")); } public List<Employee> getEmployees(){ return new ArrayList<Employee>(emps.values()); } public Employee deleteEmployees(Integer empId){ return emps.remove(empId); } }
處理完后 result到對應的頁面展示數據。
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<h1>顯示全部</h1>
</head>
<body>
<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>
<s:debug></s:debug>
</body>
</html>
二:保存
1):struts2的運行流程。
攔截器中有個parameters攔截器。它的用處是:把表單字段映射到ValueStack棧的棧頂對象的各個屬性中。如果某個字段在模型里沒有匹配的屬性。Param攔截器將嘗試把字段壓入到ValueStack棧中的下一個對象。
把表單的值賦給棧頂對象的屬性。此時棧頂對象是Action。
攔截器的順序是從第一個到最后一個依次進行。
2):ModelDriven攔截器:
作用:將ModelDriven攔截器接口的getModel()方法返回的對象置於棧頂。
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();
}
代碼實現:
package com.emp.app; import java.util.Map; import org.apache.struts2.interceptor.RequestAware; import com.opensymphony.xwork2.ModelDriven; public class EmployeeAction implements RequestAware,ModelDriven<Employee>{ private Dao dao = new Dao(); private Employee employee; public Employee getModel() { employee=new Employee(); return employee; } public String save(){ dao.save(employee); return "success"; } }
DAO層:
public void save(Employee employee){ long time=System.currentTimeMillis(); employee.setEmployeeId((int)time); emps.put(employee.getEmployeeId(), employee); }
頁面:emp-save.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<h1>添加一個</h1>
</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>
<s:debug></s:debug>
</body>
</html>
四:修改
頁面:
<%@ 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>
DAO層:
public Employee get(Integer empId){ System.out.println(empId+"aaa"); return emps.get(empId); } public void update(Employee emp){ emps.put(emp.getEmployeeId(), emp); }
先得到ID,然后修改Employee對象
package com.emp.app; import java.util.Map; import org.apache.struts2.interceptor.RequestAware; import com.opensymphony.xwork2.ModelDriven; public class EmployeeAction implements RequestAware,ModelDriven<Employee>{ private Dao dao = new Dao(); private Employee employee; public Employee getModel() {
/*判斷是創建還是修改。若是創建。employee=new Employee();
修改:employee=dao.get(employeeId);
判斷標准就是有沒有employeeId這個參數。
若通過employeeId來判斷。需要在modelDriven攔截器先執行一個params攔截器。因為params攔截器是把表單的值賦予棧頂的對象。
需要在struts.xml中配置默認的 paramsPrepareParamsStack攔截器
<default-interceptor-ref name="paramsPrepareParamsStack"></default-interceptor-ref>
*/
目的就是從棧頂中得到employee對象。然后修改的時候回顯到emp-edit.jsp頁面。 if(employeeId==null) employee=new Employee(); else employee=dao.get(employeeId); return employee; } private Integer employeeId; public void setEmployeeId(Integer employeeId) { this.employeeId = employeeId; } public String edit(){ return "edit"; } public String update(){ dao.update(employee); return "success"; } }
5):存在的問題: getModel 方法 public Employee getModel() { if(employeeId == null) employee = new Employee(); else employee = dao.get(employeeId); return employee; } I. 在執行刪除的時候, employeeId 不為 null, 但 getModel 方法卻從數據庫加載了一個對象. 不該加載! II. 指向查詢全部信息時, 也 new Employee() 對象. 浪費! . 解決方案: 使用 PrepareInterceptor 和 Preparable 接口. 7). 關於 PrepareInterceptor [分析后得到的結論] 若 Action 實現了 Preparable 接口, 則 Struts 將嘗試執行 prepare[ActionMethodName] 方法, 若 prepare[ActionMethodName] 不存在, 則將嘗試執行 prepareDo[ActionMethodName] 方法. 若都不存在, 就都不執行. 若 PrepareInterceptor 的 alwaysInvokePrepare 屬性為 false, 則 Struts2 將不會調用實現了 Preparable 接口的 Action 的 prepare() 方法 [能解決 5) 的問題的方案] 可以為每一個 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; }
使用:ModelDriven<Employee>, Preparable
package com.atguigu.struts2.app; 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>, 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中修改 PrepareInterceptor 攔截器的 alwaysInvokePrepare 屬性值為 false
<!-- 配置使用 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"/>