JpetStore中的Action與普通Struts的Action處理方式不一樣。遍歷JpetStore的src文件夾,並無一個具體的Action,那麼它是如何來完成普通Struts的Action工作了?
查看JpetStore的Struts.xml可以發現,它的Action只有一個,即“org.apache.stuts.beanaction.Beanaction”。通過Eclipse查看beanaction.jar的源代碼,可以發現Beanaction繼承與普通Action,即具備普通的action的功能。那麼它無具體Action的奧妙在哪,繼續研究BeanAction的代碼,截取BeanAction的excute方法中核心部分代碼如下:
private static final String NO_METHOD_CALL = "*";
…….
/*所有的FormBean都繼承於BaseBean*/
BaseBean bean = (BaseBean) form;
ActionContext.initCurrentContext(request, response);
if (bean != null) {
// Explicit Method Mapping
/*下面是檢查struts.xml配置中是否有parameter屬性*/
Method method = null;
String methodName = mapping.getParameter();
if (methodName != null && !NO_METHOD_CALL.equals(methodName)) {
try {
/*通過反射,根據得到的方法名稱取得方法的句柄*/
method = bean.getClass().getMethod(methodName, null);
synchronized (bean) {
/*下面是關鍵一句,調用basebean擁有的接口ActionInterceptor的實現DefaultActionInterceptor,來完成具體方法的調用*/
forward = bean.getInterceptor().intercept(new ActionInvoker(bean, method));
}
……..
/*無Parameter屬性,檢查path路徑的最後一個/後的名稱,即爲調用的方法名*/
// Path Based Method Mapping
if (method == null && !NO_METHOD_CALL.equals(methodName)) {
methodName = mapping.getPath();
if (methodName.length() > 1) {
int slash = methodName.lastIndexOf("/") + 1;
methodName = methodName.substring(slash);
if (methodName.length() > 0) {
try {
method = bean.getClass().getMethod(methodName, null);
synchronized (bean) {
forward = bean.getInterceptor().intercept(new ActionInvoker(bean, method));
}
……..
/*根據調用方法返回的String,得到頁面的轉移路徑*/
return mapping.findForward(forward);
通過研究上面這段代碼,我們可知,JpetStore中沒有具體Action實現的關鍵原因即在於下面這幾句
/*通過反射,根據得到的方法名稱取得方法的句柄*/
method = bean.getClass().getMethod(methodName, null);
synchronized (bean) {
/*下面是關鍵一句,調用basebean擁有的接口ActionInterceptor的實現DefaultActionInterceptor,來完成具體方法的調用*/
forward = bean.getInterceptor().intercept(new ActionInvoker(bean, method));
}
即將原來Action中的excute方法的實現轉移到FormBean中,這樣實現顯得更爲簡捷,方便。研究ActionInvoke,它的核心代碼如下:
public String invoke() {
try {
return (String) method.invoke(bean, null);
} catch (Exception e) {
throw new BeanActionException("Error invoking Action. Cause: " + e, e);
}
}
至此可知,它調用的是formbean中的函數。且從這段代碼可知,formbean的這類特殊函數,此處稱爲action方法,要符合兩個特徵:1)無參數;2)返回值爲string,此返回string即是Struts-config.xml的全局或局部的forward。
以上是整個beanaction的實現機制。個人感覺此種實現方法對於開發者而言已經類似於ASP.NET的.aspx與.cs開發模式了。下面是通過實例來說明一下BeanAction如何控制formbean的
Struts-config.xml的配置裏有3種映射方式,來告訴BeanAction把控制轉到哪個form bean對象的哪個方法來處理。
(1)parameter=”*’直接跳轉;(2)Parameter中含具體的方法名;(3)Path中最後一個/後的方法名
以這個請求連接爲例http://localhost/jpetstore4/shop/viewOrder.shtml
1. URL Pattern
<action path="/shop/viewOrder" type="com.ibatis.struts.BeanAction"
name="orderBean" scope="session"
validate="false">
<forward name="success" path="/order/ViewOrder.jsp"/>
</action>
此種方式表示,控制將被轉發到"orderBean"這個form bean對象 的"viewOrder"方法(行爲)來處理。方法名取"path"參數的以"/"分隔的最後一部分。
2. Method Parameter
<action path="/shop/viewOrder" type="com.ibatis.struts.BeanAction"
name="orderBean" parameter="viewOrder" scope="session"
validate="false">
<forward name="success" path="/order/ViewOrder.jsp"/>
</action>
此種方式表示,控制將被轉發到"orderBean"這個form bean對象的"viewOrder"方法(行爲)來處理。配置中的"parameter"參數表示form bean類上的方法。"parameter"參數優先於"path"參數。
3. No Method call
<action path="/shop/viewOrder" type="com.ibatis.struts.BeanAction"
name="orderBean" parameter="*" scope="session"
validate="false">
<forward name="success" path="/order/ViewOrder.jsp"/>
</action>
此種方式表示,form bean上沒有任何方法被調用。如果存在"name"屬性,則struts把表單參數等數據填充到form bean對象後,把控制轉發到"success"。否則,如果name爲空,則直接轉發控制到"success"。
這就相當於struts內置的org.apache.struts.actions.ForwardAction的功能
<action path="/shop/viewOrder" type="org.apache.struts.actions.ForwardAction"
parameter="/order/ViewOrder.jsp " scope="session" validate="false">
</action>