1). paramsPrepareParamsStack 和 defaultStack 一樣都是攔截器棧. 而 struts-default 包默認使用的是 defaultStack 2). 可以在 Struts 配置文件中通過以下方式修改使用的默認的攔截器棧 <default-interceptor-ref name="paramsPrepareParamsStack"></default-interceptor-ref> 3). paramsPrepareParamsStack 攔截器在于 params -> modelDriven -> params 所以可以先把請求參數(shù)賦給 Action 對應(yīng)的屬性, 再根據(jù)賦給 Action 的那個屬性值決定壓到值棧棧頂?shù)膶ο? 最后再為棧頂對象的屬性賦值. 對于 edit 操作而言: I. 先為 EmployeeAction 的 employeeId 賦值 II. 根據(jù) employeeId 從數(shù)據(jù)庫中加載對應(yīng)的對象, 并放入到值棧的棧頂 III. 再為棧頂對象的 employeeId 賦值(實際上此時 employeeId 屬性值已經(jīng)存在) IV. 把棧頂對象的屬性回顯在表單中. 4). 關(guān)于回顯: Struts2 表單標簽會從值棧中獲取對應(yīng)的屬性值進行回顯. 5). 存在的問題: getModel 方法 public Employee getModel() { if(employeeId == null) employee = new Employee(); else employee = dao.get(employeeId); return employee; } I. 在執(zhí)行刪除的時候, employeeId 不為 null, 但 getModel 方法卻從數(shù)據(jù)庫加載了一個對象. 不該加載! II. 指向查詢?nèi)啃畔r, 也 new Employee() 對象. 浪費! 6). 解決方案: 使用 PrepareInterceptor 和 Preparable 接口. 7). 關(guān)于 PrepareInterceptor [分析后得到的結(jié)論] 若 Action 實現(xiàn)了 Preparable 接口, 則 Struts 將嘗試執(zhí)行 prepare[ActionMethodName] 方法, 若 prepare[ActionMethodName] 不存在, 則將嘗試執(zhí)行 prepareDo[ActionMethodName] 方法. 若都不存在, 就都不執(zhí)行. 若 PrepareInterceptor 的 alwaysInvokePrepare 屬性為 false, 則 Struts2 將不會調(diào)用實現(xiàn)了 Preparable 接口的 Action 的 prepare() 方法 [能解決 5) 的問題的方案] 可以為每一個 ActionMethod 準備 prepare[ActionMethdName] 方法, 而拋棄掉原來的 prepare() 方法 將 PrepareInterceptor 的 alwaysInvokePrepare 屬性置為 false, 以避免 Struts2 框架再調(diào)用 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 是否實現(xiàn)了 Preparable 接口 if (action instanceof Preparable) { try { String[] prefixes; //根據(jù)當前攔截器的 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 //調(diào)用前綴方法. 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; } } //根據(jù)當前攔截器的 alwaysInvokePrepare(默認是 true) 決定是否調(diào)用 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(); //獲取要調(diào)用的 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, 則通過反射調(diào)用前綴方法 if (method != null) { method.invoke(action, new Object[0]); } } PrefixMethodInvocationUtil.getPrefixedMethod 方法: public static Method getPrefixedMethod(String[] prefixes, String methodName, Object action) { assert(prefixes != null); //把方法的首字母變?yōu)榇髮?/div> String capitalizedMethodName = capitalizeMethodName(methodName); //遍歷前綴數(shù)組 for (String prefixe : prefixes) { //通過拼接的方式, 得到前綴方法名: 第一次 prepareUpdate, 第二次 prepareDoUpdate String prefixedMethodName = prefixe + capitalizedMethodName; try { //利用反射獲從 action 中獲取對應(yīng)的方法, 若有直接返回. 并結(jié)束循環(huán). 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; } |
|