Struts1.3-DispatchAction类-根据请求参数实现业务分派

本文将着重来理解下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方法等。

image

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中获取。


下面是综上所述形成的一个流程图…还是画的不是很好看,将就下了。

image

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值