本文将着重来理解下org.apache.struts.actions.DispatchAction类。
在有些时候一个Action可能有多种操作,比如查询,增加,删除等,都集中在了一个Action中,每个操作都会被封装在一个独立的方法中,此时需要使用某个变量来区分Action接收到的请求需要执行哪个操作,根据操作类型再去调用相应的方法。
DispatchAction类
DispatchAction类为我们实现了这个根据请求动态的分发业务给不同的方法。
比如某个Action地址为”hello.do”,当访问”hello.do?method=add”时,将调用Action类中的add方法;访问”hello.do?method=show”时,将调用Action类中show方法。
这里的请求路径参数”?method=add”,
前者参数名”method”需要在struts-config.xml配置文件中的action元素中使用parameter属性定义,
后者参数值对应着Action类中的同名方法。
我们需要将自己的Action继承DispatchAction类,DispacthAction类又是继承Action类的。然后需要声明自己的业务处理方法,
public class HelloAction extends DispatchAction { public ActionForward add (ActionMapping mapping , ActionForm form , HttpServletRequest request , HttpServletResponse response) { System.out.println("HelloAction.add()"); return null; } public ActionForward show (ActionMapping mapping , ActionForm form , HttpServletRequest request , HttpServletResponse response) { System.out.println("HelloAction.show()"); return null; } }
这里需要注意的是,自定义的业务处理方法的格式为:
public ActionForward methodName (ActionMapping mapping , ActionForm form , HttpServletRequest request , HttpServletResponse response)
{
…
}
方法的返回类型与方法参数都是固定的,方法名可以自定义。
对应的struts-config.xml文件中action元素:
<action attribute="helloForm" input="/index.jsp" name="helloForm" path="/hello" scope="request" type="com.yourcompany.struts.action.HelloAction" cancellable="yes" parameter="method" />
最后一个parameter属性是关键。
当访问 “/hello.do?method=add”时,将调用add方法,输出”HelloAction.add()”
当访问”/hello.do?method=show”时,将调用show方法,输出”HelloAction.show()”
上面的例子是比较简单的一个DispacthAction。我们还可以重写DispacthAction类中的某些方法,比如unspecified方法,cancelled方法,execute方法等。
cancelled方法,默认方法内容为空,返回null
protected ActionForward cancelled (ActionMapping mapping , ActionForm form , HttpServletRequest request , HttpServletResponse response) throws Exception { return null; }
当JSP页面上的<html:cancel />被按下时,cancelled方法被调用,可以重写这个方法,实现cancel按钮点击的事件处理。
@Override public ActionForward cancelled (ActionMapping mapping , ActionForm form , HttpServletRequest request , HttpServletResponse response) throws Exception { System.out.println("HelloAction.cancelled()"); ... return mapping.findForward("indexPage");//返回主页 }
unspecified方法,如果当前Parameter名与配置中的parameter属性值不一样时,被调用。
比如访问地址 “/hello.do?action=add”,因为配置文件的Action元素的parameter属性值为”method”,与action不符合,此时将调用unspecified方法。
如果是参数值不同,找不到相应的方法,将抛出NoSuchMethodException异常。
默认的方法内容是直接抛出异常的:
protected ActionForward unspecified (ActionMapping mapping , ActionForm form , HttpServletRequest request , HttpServletResponse response) throws Exception { String message = messages.getMessage("dispatch.parameter" , mapping.getPath() , mapping.getParameter()); //message:Request[{0}] does not contain handler parameter named '{1}'. This may be caused by whitespace in the label text. log.error(message); throw new ServletException(message); }
execute方法,请求当前Action时,首先被调用的方法。关于方法内部细节,请看注释。
public ActionForward execute (ActionMapping mapping , ActionForm form , HttpServletRequest request , HttpServletResponse response) throws Exception { if (isCancelled(request)) { // 如果已经退出,调用cancelled方法。 ActionForward af = cancelled(mapping , form , request , response); // 如果返回值不为null,则直接return,否则如果为null,继续执行后面的代码。 if (af != null) { return af; } } // 获取参数名,使用ActionMapping.getParameter()方法,如果参数名为null,在getParameter方法内部会抛出ServletException异常。 String parameter = getParameter(mapping , form , request , response); // 根据参数名获取参数值 使用request.getParameter(parameter) 方法,获得希望要访问的方法名 String name = getMethodName(mapping , form , request , response , parameter); // 方法名不能为execute或者perform,否则将抛出ServletException异常。 if (("execute".equals(name)) || ("perform".equals(name))) { String message = messages.getMessage("dispatch.recursive" , mapping.getPath()); log.error(message); throw new ServletException(message); } //调用dispatchMethod方法,参数name为希望要访问的方法名,在dispatchMethod方法内部将使用反射机制去调用与name同名的方法。 //如果name==null,将调用unspecified方法,并直接返回。 return dispatchMethod(mapping , form , request , response , name); }
我们可以重写execute方法,在利用反射调用相应的方法前,对mapping,form,request,response做一定的操作,最后再:
return super.execute(mapping,form,request,response);
使用Myeclipse自动生成的Action类,一般都会默认生成一个execute方法,此时必须手动修改代码(或者直接删除execute方法),调用super.execute方法,不然覆盖了父类的execute方法将导致分发功能失效。
getParameter方法,获取配置文件中的参数名。如果参数名为null,将抛出异常。
@Override protected String getParameter (ActionMapping mapping , ActionForm form , HttpServletRequest request , HttpServletResponse response) throws Exception { // 获取配置文件中parameter属性值 String parameter = mapping.getParameter(); // 如果为null,抛出ServletException异常。 if (parameter == null) { String message = messages.getMessage("dispatch.handler" , mapping.getPath()); // DispatchMapping[{0}] does not define a handler property log.error(message); throw new ServletException(message); } return parameter; }
getMethodName方法,根据参数名获得希望访问的方法名。
@Override protected String getMethodName (ActionMapping mapping , ActionForm form , HttpServletRequest request , HttpServletResponse response , String parameter) throws Exception { return request.getParameter(parameter); }
注意,在这里使用的parameter是从配置文件的parameter属性中获取的,并不是地址栏链接参数中等于号前面的参数名。
所以,如果配置文件中的parameter值和实际访问地址中的参数名不相同的话,返回的String为null。这个null值将传递到dispatchMethod方法…
dispatchMethod方法,方法内部使用反射调用相应的方法。具体细节,请看注释。
protected ActionForward dispatchMethod (ActionMapping mapping , ActionForm form , HttpServletRequest request , HttpServletResponse response , String name) throws Exception { // 接在execute方法内部传过来的name,如果为null,将调用unspecified方法,并直接return。 if (name == null) { return unspecified(mapping , form , request , response); } Method method = null; try { // 根据name值,获得一个Method对象,如果未找到相应的方法,将抛出NoSuchMethodException异常。 method = getMethod(name); } catch (NoSuchMethodException e) { String message = messages.getMessage("dispatch.method" , mapping.getPath() , name); log.error(message , e); String userMsg = messages.getMessage("dispatch.method.user" , mapping.getPath()); throw new NoSuchMethodException(userMsg); } ActionForward forward = null; try { // 封装4个参数为Object数组,在利用反射调用方法时作为参数传递。 Object [] args = { mapping , form , request , response }; // 利用上面获得的Method对象使用invoke从当前类中调用方法。 // 返回的对象被强转为ActionForward。 forward = (ActionForward) method.invoke(this , args); } catch (ClassCastException e) {// 当invoke方法返回的对象 无法强转为ActionForward时抛出。 String message = messages.getMessage("dispatch.return" , mapping.getPath() , name); log.error(message , e); throw e; } catch (IllegalAccessException e) {// 无法访问 方法时抛出。在自定义方法时,方法不能为私有。 String message = messages.getMessage("dispatch.error" , mapping.getPath() , name); log.error(message , e); throw e; } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); if ((t instanceof Exception)) { throw ((Exception) t); } String message = messages.getMessage("dispatch.error" , mapping.getPath() , name); log.error(message , e); throw new ServletException(t); } // 最后返回我们自定义方法的返回结果 return forward; }
getMethod方法,根据方法名获取Method对象:
protected Class clazz = getClass(); protected HashMap methods = new HashMap(); protected Class[] types = { ActionMapping.class, ActionForm.class, HttpServletRequest.class, HttpServletResponse.class }; @Override protected Method getMethod (String name) throws NoSuchMethodException { synchronized (this.methods) { Method method = (Method) this.methods.get(name);//根据name直接从HashhMap中直接获得Method对象 if (method == null)//如果为null { method = this.clazz.getMethod(name , this.types);//获取Method对象 this.methods.put(name , method);//保存在HashMap中。 } return method; } }
第一次访问时,会将获取的Method对象保存在hashMap中,下次获取时直接从HashMap中获取。
下面是综上所述形成的一个流程图…还是画的不是很好看,将就下了。