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) {
}
}
这样,主要的地方是修改了控制器,别的地方都没有修改,就完成了方法的分发。