爲了解決上節的問題
I. 在執行刪除的時候, employeeId 不爲 null, 但 getModel 方法卻從數據庫加載了一個對象. 不該加載!
II. 指向查詢全部信息時, 也 new Employee() 對象. 浪費!
使用PrepareInterceptor攔截器
可以爲每一個 ActionMethod 準備 prepare[ActionMethdName] 方法, 而拋棄掉原來的 prepare() 方法
將 PrepareInterceptor 的 alwaysInvokePrepare 屬性置爲 false, 以避免 Struts2 框架再調用 prepare() 方法.
在struts.xml中設定不執行接口prepa
<param name="prepare.alwaysInvokePrepare">false</param>
EmployeeAction類
public class EmployeeAction implements RequestAware, ModelDriven<Employee>, Preparable{
private Dao dao = new Dao();
/**
* 使用ModelDriven而不在Aciton中聲明employee各個對象
*/
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";
}
/**
* 實現Preparable接口後,可以設定前綴方法prepare+methodName,來在getModel之前準備
*/
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;
}
//需要在當前EmployeeAction定義id屬性,以接收頁面請求參數
private Integer employeeId;
public void setEmployeeId(Integer employeeId) {
this.employeeId = employeeId;
}
/**
* 在新建和修改時會用到
*/
@Override
public Employee getModel() {
//若create 就new一個
//若修改,則從數據庫取
//判斷標準爲是否有EmployeeID參數
//所以在進入DrivelModel攔截器前要先執行params攔截器棧
//通過paramsPrepaeParams攔截器棧可以實現
return employee;
}
@Override
public void prepare() throws Exception {
System.out.println("prepare...");
}
}
struts2源代碼解析
----------------------------------源代碼解析---------------------------------
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;
}