天天看点

深入解析Struts控制器组件

1.引言

关于Struts项目,在以前的文章中我已经做过一些介绍,在此不再赘述。写本文,旨在从控制器的层面进一不加深对Struts MVC模型的理解和认识.

2.进入主题

Struts控制器组件负责接收用户请求、更新模型,以及选择合适的视图组件返回给用户。控制器组件有助于将模型层和视图层分离,有了之中分离,就可以在同一个模型的基础上得心应手的开发多种类型的视图。

Struts控制器组件主要包括:

ActionServlet组件:充当Struts框架的中央控制器。

RequestProcessor组件:充当每个子应用模块的请求处理器。

Action组件:负责处理一项具体的业务。

Struts框架采用ActionServlet和RequestProcessor组件进行集中控制,并采用Action组件来处理单项业务。之中控制方式就能够满足MVC设计模式的两大需求:

首先,控制器的用户输入数据和模型层之间充当媒介/翻译者的角色,提供一些通用的功能,当系统的这些功能出现需求变更时,不需要修改整个应用,只需要修改

局部的控制器组件即可。

其次,由于所以的请求都经过控制器的过滤,因此可以降低视图组件之间,以及视图组件和模型组件之间的相互依赖关系,提高每个组件的相对独立性。由控制器组件来决定把合适的视图组件返回给用户,这可以减少视图组件之间直接的、错综复杂的链接关系,使得应用更加灵活便于维护。

2.1.控制器组件的控制机制

Struts的控制器组件主要完成以下任务:

接收用户请求。

根据用户请求,调用相应的模型组件来执行相应的业务逻辑。

获取业务逻辑执行结果。

根据当前状态以及业务逻辑执行结果,选择合适的视图组件返回给用户。

在Struts组件中,由如下一系列相关联的组件来共同完成控制任务。

2.1.1.ActionServlet类

org.apache.struts.action.ActionServlet类是Struts框架的核心控制器组件,所有的用户请求都先由ActionServlet来处理,然后在由它把请求转发给其他组件。Struts框架只允许早一个应用中配置一个ActionServlet类,在应用的生命周期中,仅创建ActionServlet类的一个实例,这个AtionServlet实例可以同时响应多个用户请求。

2.1.2.RequestProcessor类

对于多运用模块的Struts应用,每个子应用模块都有各自的RequestProcessor实例.在ActionServlet的process()方法中,一旦选择了正确的子应用模块,就会调用子应用模块RequestProcessor实例的的process()方法来处理请求.在ActionServlet调用这个方法时,会把当前的request和response对象传给它.Struts框架只允许应用中只存在一个ActionServlet实例,但是可以存在多个RequestProcessor类,每个子应用都有单独的RequestProcessor类.

2.1.3.Action类

Action类是用户请求和业务逻辑之间的桥梁.每个Action充当客户的一项业务代理.在RequestProcessor类预处理请求时,在创建了Action的实例后,就调用自身的processActionperform()方法,该方法再调用Action类的execute()方法.Action类的execute()方法再调用模型组件的业务方法,完成用户请求的业务逻辑处理,然后根据执行结果把请求转发给合适的Web组件.

为了确保线程安全,在一个应用的生命周期中,Struts框架只会为每个Action类创建一个实例.所有的客户请求共享一个实例,并且所有请求线程可以同时执行它的execute()方法.

2.1.4.ActionForward类

Action类的execute()方法返回一ActionForwad对象.ActionForward对象代表了Web资源的逻辑抽象.这里的资源可以是JSP页/Java Servlet或Action.从execute()方法中返回ActionForward对象有两种方法:

第一:在execute()方法中,动态创建一个ActionForward实例,

return new ActionForward("Failure","/security/error.jsp","true");

以上ActionForward构造方法的第一个参数代表ActionForward实例的逻辑名,第二个参数指定转发路径,第三个参数指定是否进行重定向.(true:重定向;false:请求转发)

第二:在Struts配置文件中配置<forward>元素.

    <action name="loginActionForm"

            parameter="reqCode"

            path="/login"

            scope="request"

            validate="false"

            type="com.eRedLab.eRedCIP.frame.web.LoginAction">

              <forward name="Failure" path="/security/error.jsp" />

    </action>

配置了<forward>元素后,在Struts框架初始化时就会创建存放<forasrd>元素配置信息的ActionForward对象.在Action的execute方法中只需调用Actionmapping实例的findForward()方法,来获得特定的ActionForward实例:

return mapping.findForward("Failure");

采用第二中方法,无需在程序中硬编码来指定转发资源的物理路径.而是在配置文件中配置转发资源,程序中只需要引用转发资源的逻辑名即可.这有效提高了应用的灵活性和可维护性.

2.2.使用内置的Struts Action类

Struts提供了一些现成的Action类,在Struts应用中直接使用这些Action类可以大大节省开发时间.下面就将介绍几个最常用的Action类:

2.2.1.org.apache.struts.actions.ForwardAction类

在JSP网页中,尽管可以直接通过<jsp:forward>标签把请求转发给其他Web组件,但是Struts框架提倡先把请求转发给控制器,再由控制器来负责请求转发.

由控制器来负责请求转发有一下一些优点:

2.2.1.1.控制器具有预处理请求功能,它能够选择正确的子应用模块来处理请求,并且把子应用模块的MoudleConfig和MessageResources对象存放在Request范围内.这样,请求转发的目标WEB组件就可以正常的访问MoudleConfig和MessageResources对象.

2.2.1.2.如果JSP页面中包含HTML表单,那么控制器能够创建和这个表单对应的ActionForm对象,把用户输入的表单数据组装到ActionForm中.如果<action>节点的validate属性为true,那么还会调用ActionForm的表单验证方法.控制器把ActionForm对象存放在request或session范围内,这样请求转发的目标Web组件也可以访问ActionForm.

2.2.1.3.JSP网页之间直接相互转发违背了MVC的分层原则.按照MVC设计思想,控制器负责处理所有请求,然后选择恰当的视图组件返回给用户.

对于用户自定义的Action类,既可以负责请求转发,还可以充当客户端的业务代理.如果仅仅需要Action类提供请求转发功能,则可以使用ForwarAction类.ForwardAction专门用于请求转发,不执行任何业务操作.

2.2.2.org.apache.struts.actions.DispatchAction类

通常在一个Action中只能完成一种业务操作,如果希望在同一个Action中完成一组相关的业务操作,就要使用DispatchAction类.

创建一个扩展DispatchAction的子类,不必覆盖execute()方法,而是创建一些实现业务操作的方法,这些业务方法都应该和execute()方法具有同样的业务签名,即她们的参数和返回类型都相同.此外,也应该声明抛出Exception.

在配置DispatchAction类时,需要把parameter属性设置为"method":

    <action

        name="testTagNo1ActionForm"

        parameter="method"

        path="/testTagNo1"

        scope="request"

        type="com.eRedLab.eRedCIP.test.web.TestTagNo1Action">

     <forward name="jspView" path="/testTagNo1.jsp" />

    </action>

把parameter的属性设置为"method"后,当用户请求访问DispatchAction时,应该提供method请求参数.例如:

http://localhost:8080/eredcip/testTagNo1.do?method=addItem&id=2

以上method请求参数值为:"addItem",它指定了需要调用的业务方法,因此DispatchAction将调用相应的addItem()方法.

2.2.3.org.apache.struts.actions.LookupDispatchAction类

LookupDispatchAction类是DispatchAction的子类.在LookupDispatchAction中也可以定义多个业务方法.通常LookupDispatchAction主要用于在一个表单中有多个提交按钮,而这些按钮又有一个共同的名字的场合.这些按钮的名字和具体的ActionMapping的parameter属性值对应.下面举例说明如何使用LookupDispatchAction类:

首先,在同一个表单中定义多个提交按钮.如下:Test1.jsp

<html:form action="/processcheckout">

......

<html:submit property="action">

<bean:message key="button.save">

</html:submit>

<html:submit property="action">

<bean:message key="button.checkout">

</html:submit>

</html:form>

以上JSP代码的输出内容为:

<form name="checkoutForm" method="post" action="security/processcheckout.do">

......

<input type="submit" name="action" value="save"/>

<input type="submit" name="action" value="checkout"/>

</form>

接着,创建一个扩展LookupDispatchAction类得子类.

在ProcessCheckoutAction类中实现getKeyMethodMap()方法,这个方法返回包含key/value数据的java.util.Map对象.Map对象中的Key和<bean:message>标签的key属性匹配.value和LookupDispatchAction子类的业务方法名匹配.

以下为ProcessCheckOutAction类的getKeyMethodMap()方法:

protected Map getKeyMethodMap(){

    Map map = new HashMap();

    map.put("button.checkout","checkout");

    map.put("button.save","save");

    return map;

}

根据以上程序,应该在ProcessChecoutAction中提供两个业务方法checkout()和save();当用户提交[checkout]按钮,Shtuts框架就会调用checkout()方法;当用户提交[save]按钮时,就会调用save()方法.这些业务方法都应该和execute()方法有同样的方法签名,即它们的参数和返回类型都应该相同.业务方法声明抛出的异常为:

IOException和ServletException.

最后,在ProcessCheckoutAction的配置文件中,应该把<action>元素的parameter属性值设置为action.使它和<html:submit>标签的property属性保持一致.

3.结束语

本文从控制器的角度深入介绍了构成Struts框架的中央控制器ActionServlet和RequestProcessor的控制流程.一个Struts应用只能有一个ActionServlet实例,对于每和子应用模块都有各自的RequestProcessor实例.在Web应用描述符文件中的ActionServlet初始化阶段负责加载每个子应用模块的Struts配置文件,并把所有的配置信息保存到内存中相关配置类的实例中.

继续阅读