Struts 1 中關于Action 的方法分發dispatch
在struts1中,我們知道在客戶端發送“url.do”時由前端控制器ActionServlet分派給RequestProcessor來處理客戶請求,最后分發給相應的應用程序控制器Action來處理業務,那么在一個應用程序中,如果有n個業務請求就需要n個action來處理,相應就需要n個action類文件,一個Action中只有一個execute方法,如果我們把某一個功能模塊的所有方法放在一個Action類文件中,把不同的業務放在不同的方法中,并且由ActionServlet統一進行分發,豈不妙哉!在struts1中DispatchAction給我們提供了解決方法。比如:
定義
public abstract class DispatchAction extends Action
這是一個抽象的Action,它會根據request 中的parameter來執行相應的方法。通個這個Action類可以將不同的Action集中到一個Action文件中來
Struts-config.xml:
<action path="/saveSubscription"
type="org.apache.struts.actions.DispatchAction"
name="subscriptionForm"
scope="request"
input="/subscription.jsp"
parameter="method"/>
在Action中要有相應的方法:
Public class TestAction extends DispatchAction{
public ActionForward delete(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception
public ActionForward insert(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception
public ActionForward update(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception
}
你就可以通過這樣的方法來訪問你的程序:
http://localhost:8080/myapp/saveSubscription.do?method=update
這樣,我們就完成了action中方法的分發。可以大大減少action文件的數量。
一直以來,我個人不太喜歡DispatchAction的做法:
1、 把action的方法名稱暴露在界面上,給人的感覺總是有種“上身穿西裝,下神是裸體”的感覺。
2、 如果閱讀一個請求,需要從界面的請求開始看起,經歷struts-config.xml然后才能到對應的action方法,感覺太麻煩。
3、 如果一個action中的請求中使用不同的form對象,在這樣的程序中就感覺非常難受。
4、 DispatchAction使用execute做進一步的方法分發,如果我們使用DispatchAction,就不能使用execute方法了。
5、 如果方法的分發不是由界面決定的,只是由struts-config.xml決定的就感覺好多了。
盡管有一個LookupDispatchAction 可以根據界面不同按鈕進行分發,感覺還不是很滿意,struts1.2中又提出了MappingDispatchAction,這個action比較符合我的想法,但是MappingDispatchAction是繼承自DispatchAction,骨子里還是DispatchAction,照樣不能使用默認的execute方法。
有沒有更好的辦法既讓執行默認的execute方法,又可以根據配置文件來執行parameter規定的方法呢?
也許有,我還不知道,我也不想去找了,干脆我來做一個,懶得費精力去搜索:
理論:
當客戶端發送請求“url.do”到ActionServlet時,servlet會調用RequestProcessor來處理客戶端的請求,RequestProcessor類中有很多processXXX方法:
ProcessPreProcess 最先調用的方法,決定struts是否繼續向下處理。
processPath 獲取客戶端的請求路徑
processMapping 利用路徑來獲得相應的ActionMapping
processActionForm 初始化ActionForm(如果需要)并存入正確的scope中
processActionCreate 初始化Action
processActionPerform 調用Action的execute方法
processForwardConfig 處理Action返回的ActionForward
其中processActionPerform 是調用Action的execute方法,我們重新寫這個方法利用反射就可以搞定我們想要的操作。
具體的代碼如下:
public class DispatchRequestProcessor extends RequestProcessor {
//規定action中的方法參數類型,必須與execute方法的參數類型一致。返回值必須是ActionForward
final Class[] types=new Class[]{ActionMapping.class,
ActionForm.class,
HttpServletRequest.class,
HttpServletResponse.class};
@Override
protected ActionForward processActionPerform(
HttpServletRequest request,
HttpServletResponse response,
Action action,
ActionForm form,
ActionMapping mapping) throws IOException, ServletException {
try {
//從struts-config.xml中的parameter屬性中獲取需要調用的方法名稱,如果方法名不存在,就調用默認的execute方法。
String methodName=mapping.getParameter();
if (methodName==null || methodName.length()==0)
{
return action.execute(mapping, form, request, response);
}
//利用反射找到需要調用的action中的方法Method
Method method=action.getClass().getMethod(methodName, types);
//調用action中方法時準備的方法參數
Object[] args=new Object[]{
mapping,
form,
request,
response
};
//調用action中的方法,結果result就是ActionForward對象
Object result=method.invoke(action, args);
return (ActionForward) result;
} catch (Exception e) {
e.printStackTrace();
response.sendError(500,e.getMessage());
}
return null;
}
}
這個控制器就可以從配置文件中讀取action中要調用的方法,如果方法沒有存在還可以自動調用execute方法。與界面沒有任何關系,界面的請求還是普通的action請求,配置文件需要修改。
例如:
<action
attribute="userForm"
input="/form/user.jsp"
name="userForm"
path="/userAdd"
scope="request"
parameter="add"
type="com.yourcompany.struts.action.UserAction" >
<forward name="ok" path="/userSelect.do" />
</action>
<action
attribute="userForm"
name="userForm"
parameter="update"
path="/userUpdate"
scope="request"
type="com.yourcompany.struts.action.UserAction">
<forward name="ok" path="/userSelect.do" />
</action>
<!--沒有parameter屬性,默認處理,將調用execute方法 -->
<action
path="/userSelect"
type="com.yourcompany.struts.action.UserAction">
<forward name="ok" path="/ok.jsp" />
</action>
對應的Action變化是,不但有默認的execute方法,還可以有其他的方法。
public class UserAction extends Action {
//處理struts-config.xml中沒有parameter屬性的操作 ,select
public ActionForward execute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) {
}
public ActionForward add(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) {
}
public ActionForward update(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) {
}
}
這樣,主要的地方是修改了控制器,別的地方都沒有修改,就完成了方法的分發。