风故故,也依依

Stand still in the wind.

导航

统计

公告

Spring整合Struts(2)

Struts的plug-in配置部分明确指出,Spring的配置文件有两个:applicationContext.xml和action-servlet.xml。其实,完全可以使用一个配置文件。通常,习惯将Action Bean配置在控制器的context内。action-servlet.xml用于配置表现层上下文,其详细配置信息如下:

<?xml version="1.0" encoding="gb2312"?>

<!-- 指定Spring配置文件的根元素,以及对应的Schame信息 -->

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 每个request请求产生一个新实例,将所有该请求的作用域配置成request -->

    <bean name="/login" class="lee.LoginAction" scope="request">

        <property name="vb" ref="vb"/>

    </bean>

</beans>

因为每次请求都应该启动新的Action处理用户请求,因此,应将Action的作用域配置成Request。

注意:ActionServlet转发请求时,是根据Bean的name属性,而不是id属性。因此,此处确定的name属性与Struts的action属性相同。

applicationContext.xml只有一个bean配置,即vb bean。其详细配置如下:

<?xml version="1.0" encoding="GBK"?>

<!-- 指定Spring 配置文件的根元素,以及对应的Schema信息 -->

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 配置ValidBean实例 -->

    <bean id="vb" class="lee.ValidBeanImpl"/>

</beans>

ValidBeanImpl是一个业务逻辑bean,本示例程序中仅作简单的判断,ValidBeanImpl的源代码如下:

//面向接口编程,实现ValidBean接口

public class ValidBeanImpl implements ValidBean

{

    //根据输入的用户名和密码判断是否有效

    public boolean valid(String username,String pass)

    {

        //有效,返回true

        if (username.equals("scott") && pass.equals("tiger"))

        {

            return true;

        }

        return false;

    }

}

注意:上面的业务逻辑组件非常简单,它只是一个示意。如果是真实的应用,业务逻辑组件应该通过DAO组件来实现业务逻辑方法。

应用的业务逻辑控制器,Action则负责调用业务逻辑组件的方法,并根据业务逻辑组件方法的返回值,确定如何响应用户请求。下面是该示例应用控制器的代码:

//业务控制器继承Action

public class LoginAction extends Action

{

    //action控制器将调用的业务逻辑组件

    private ValidBean vb;

    //依赖注入业务逻辑组件的setter方法

    public void setVb(ValidBean vb)

    {

        this.vb = vb;

    }

    //必须重写该核心方法,该方法actionForm将表单的请求参数封装成值对象

    public ActionForward execute(ActionMapping mapping, ActionForm form,

       HttpServletRequest request, HttpServletResponse response)throws
       Exception

    {

        //form由ActionServlet转发请求时创建,封装了所有的请求参数

        LoginForm loginForm = (LoginForm)form;

        //获取username请求参数

        String username = loginForm.getUsername();

        //获取pass请求参数

        String pass = loginForm.getPass();

        //下面为服务器端的数据校验

        String errMsg = "";

        //判断用户名不能为空

        if (username == null || username.equals(""))

        {

            errMsg += "您的用户名丢失或没有输入,请重新输入";

        }

        //判断密码不能为空

        else if(pass == null || pass.equals(""))

        {

            errMsg += "您的密码丢失或没有输入,请重新输入";

        }

        //如果用户名和密码不为空,才调用业务逻辑组件

        else

        {

            //vb是业务逻辑组件,由容器注入

            if (vb.valid(username,pass))

            {

                return mapping.findForward("welcome");

            }

            else

            {

                errMsg = "您的用户名和密码不匹配";

            }

        }

        //判断是否生成了错误信息

        if (errMsg != null && !errMsg.equals(""))

        {

            //如果有错误信息,将错误信息保存在request里,并跳转到input对应的
            forward对象

            request.setAttribute("err" , errMsg);

            return mapping.findForward("input");

        }

        else

        {

            //如果没有错误信息,跳转到welcome对应的forward对象

            return mapping.findForward("welcome");

        }

    }

}

在本应用中,使用了Struts的客户端数据校验,让Action继承ValidatorActionForm即可。ActionForm的代码非常简单,此处不再赘述。

为了完成数据校验,还应该编写数据校验规则文件。在struts-config.xml文件的尾部,另有一个plug-in用来加载校验文件,其中validator-rules.xml文件位于struts压缩包的lib下,直接复制过来即可使用,而validator.xml必须自己编写,validator.xml文件如下:

<?xml version="1.0" encoding="GBK"?>

<!-- 验证规则文件的文件头,包括DTD等信息 -->

<!DOCTYPE form-validation PUBLIC

          "-//Apache Software Foundation//DTD Commons Validator Rules
          Configuration 1.1.3//EN"

          "http://jakarta.apache.org/commons/dtds/validator_1_1_3.dtd">

<!-- 验证文件的根元素 -->

<form-validation>

    <!-- 所有需要验证的form都放在formset里 -->

<formset>

    <!-- 需要验证的form名,该名与struts里配置的名相同 -->

    <form name="loginForm">

        <!-- 指定该form的username域必须满足的规则:必填、模式匹配 -->

        <field property="username" depends="required,mask">

            <arg key="loginForm.username" position="0"/>

            <var>

                <!-- 确定匹配模式的正则表达式 -->

            <var-name>mask</var-name>

            <var-value>^[a-zA-Z]+$</var-value>

            </var>

        </field>

        <!-- 指定该form的pass域必须满足的规则:必填 -->

        <field property="pass" depends="required">

            <msg name="required" key="pass.required"/>

            <arg key="loginForm.pass" position="0"/>

        </field>

    </form>

</formset>

</form-validation>

上面示例程序的结构非常清晰:表现层组件(Action)配置在action-servlet.xml文件中,而业务逻辑层组件(vb)配置在applicationContext.xml文件中,如果应用中有DAO组件,将DAO组件配置在dao-context.xml文件中。将3个文件放在plug-in元素里一起加载。

文本框:图6.3  DelegatingRequestProcessor整合策略的登录失败效果DelegatingRequestProcessor会将请求转发到Action,该Action已经处于IoC容器管理之下,因此,可以方便地访问容器中的其他Bean。

通过配置文件可以看出,Action根本无须type属性,即struts-config.xml中Action根本没有实例化过,DelegatingRequestProcessor将请求转发给Spring容器中的同名Bean。这种转发的时机非常早,避免了创建struts-config.xml配置文件中的Action,因而性能非常好。

图6.3是采用这种整合策略的执行效果。

6.4.4 使用DelegatingActionProxy

使用DelegatingRequestProcessor简单方便,但有一个缺点,RequestProcessor是Struts的一个扩展点,也许应用程序本身就需要扩展RequestProcessor,而DelegatingRequest- Processor已经使用了这个扩展点。

为了重新利用Struts的RequestProcessor这个扩展点,有两个做法:

   ● 应用程序的RequestProcessor不再继承Struts的RequestProcessor,改为继承DelegatingRequestProcessor。

   ● 使用DelegatingActionProxy。

前者常常有一些未知的风险,而后者是Spring推荐的整合策略。使用Delegating- ActionProxy与DelegatingRequestProcessor的目的都只有一个,将请求转发给Spring管理的Bean。

DelegatingRequestProcessor直接替换了原有的RequestProcessor,在请求转发给action之前,转发给Spring管理的Bean;而DelegatingActionProxy则被配置成Struts的Action,即所有的请求先被ActionServlet截获,请求被转发到对应的Action,而action的实现类全都是DelegatingActionProxy,DelegatingActionProxy再将请求转发给Spring容器的Action。

可以看出,使用DelegatingActionProxy比使用DelegatingRequestProcessor要晚一步转发到Spring的context。但通过这种方式可以避免占用扩展点。

与使用DelegatingRequestProcessor相比,使用DelegatingActionProxy仅需要去掉controller配置元素,并将所有的action实现类改为DelegatingActionProxy即可。详细的配置文件如下:

<!-- XML文件的版本和编码集 -->

<?xml version="1.0" encoding="gb2312"?>

<!-- struts配置文件的文件头,包括DTD等信息 -->

<!DOCTYPE struts-config PUBLIC

          "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN"

          "http://struts.apache.org/dtds/struts-config_1_2.dtd">

<!-- struts配置文件的根元素 -->

<struts-config>

    <!-- 配置formbean,所有的formbean都放在form-beans元素里定义 -->

    <form-beans>

        <!-- 定义了一个formbean,确定formbean名和实现类 -->

        <form-bean name="loginForm" type="lee.LoginForm"/>

    </form-beans>

    <!-- 定义action部分,所有的action都放在action-mapping元素里定义 -->

    <action-mappings>

        <!-- 这里只定义了一个action。必须配置action的type元素为
        DelegatingActionProxy -->

        <action path="/login" type="org.springframework.web.struts.
        DelegatingActionProxy"

            name="loginForm" scope="request" validate="true" input=
            "/login.jsp" >

            <!-- 定义action内的两个局部forward元素 -->

            <forward name="input" path="/login.jsp"/>

            <forward name="welcome" path="/welcome.html"/>

        </action>

    </action-mappings>

    <!-- 加载国际化的资源包 -->

    <message-resources parameter="mess"/>

    <!-- 装载验证的资源文件 -->

    <plug-in className="org.apache.struts.validator.ValidatorPlugIn">

        <set-property property="pathnames" value="/WEB-INF/validator-
        rules.xml,/WEB-INF/validation.xml" />

        <set-property property="stopOnFirstError" value="true"/>

    </plug-in>

    <!-- 装载Spring配置文件,随应用启动创建ApplicationContext实例 -->

    <plug-in className="org.springframework.web.struts. ContextLoaderPlugIn">
        <set-property property="contextConfigLocation"

            value="/WEB-INF/applicationContext.xml,

                   /WEB-INF/action-servlet.xml"/>

    </plug-in>

</struts-config>

DelegatingActionProxy接收ActionServlet转发过来的请求,然后转发给Application- Context管理的Bean,这是典型的链式处理。

通过配置文件可以看出,struts-config.xml文件中配置了大量DelegatingActionProxy实例,Spring容器中也配置了同名的Action。即Struts的业务控制器分成了两个部分:第一个部分是Spring的DelegatingActionProxy,这个部分没有实际意义,仅仅完成转发;第二个部分是用户的Action实现类,该实现类负责真实的处理。

这种策略的性能比前一种策略的效果要差一些,因为需要多创建一个Delegating- ActionProxy实例。而且,J2EE应用中Action非常多,这将导致大量创建DelegatingActionProxy实例,使用一次之后,等待垃圾回收机制回收——这对性能的影响不可避免。

图6.4是DelegatingActionProxy的执行效果。

文本框:图6.4  DelegatingActionProxy整合策略的登录成功效果注意:使用DelegatingActionProxy的整合策略,可避免占用Struts的RequestProcessor扩展点,但降低了整合性能。

6.4.5 使用ActionSupport代替Action

前面已经介绍了,Spring与Struts的整合还有一种策略,让Struts的Action显式获取Spring容器中的Bean。在这种策略下,Struts的Action不接受IoC容器管理,Action的代码与Spring API部分耦合,造成代码污染。这种策略也有其好处:代码的可读性非常强,Action的代码中显式调用业务逻辑组件,而无须等待容器注入。

Action中访问ApplicationContext有两种方法:

   ● 利用WebApplicationContextUtils工具类。

   ● 利用ActionSupport支持类。

通过WebApplicationContextUtils,可以显式获得Spring容器的引用(请参阅6.4.1节的内容),而ActionSupport类则提供了一个更简单的方法getWebApplicationContext(),该方法可直接获取Spring容器的引用。

所谓ActionSupport类,是指Spring提供了系列扩展。Spring扩展了Struts的Action,在Struts的Action后加上Support后缀,Spring扩展的Action有如下几个:

   ● ActionSupport。

   ● DispatchActionSupport。

   ● LookupDispatchActionSupport。

   ● MappingDispatchActionSupport。

下面的示例将展示这种整合策略,在这种整合策略下,Struts的Action改为继承Spring扩展后的Action,下面是应用的Action代码:

//新的业务控制器,继承Spring的ActionSupport类

public class LoginAction extends ActionSupport

{

    //依然将ValidBean作为成员变量

    private ValidBean vb;

    //构造器,注意:不可在构造器中调用getWebApplicationContext()方法

    public LoginAction()

    {

    }

    //完成ValidBean的初始化

    public ValidBean getVb()

    {

        return(ValidBean)getWebApplicationContext().getBean("vb");

    }

    //必须重写该核心方法,该方法actionForm将表单的请求参数封装成值对象

    public ActionForward execute(ActionMapping mapping, ActionForm form,

       HttpServletRequest request, HttpServletResponse response)throws
       Exception

    {

        //form由ActionServlet转发请求时创建,封装了所有的请求参数

        LoginForm loginForm = (LoginForm)form;

        //获取username请求参数

        String username = loginForm.getUsername();

        //获取pass请求参数

        String pass = loginForm.getPass();

        //下面为服务器端的数据校验

        String errMsg = "";

        //判断用户名不能为空

        if (username == null || username.equals(""))

        {

            errMsg += "您的用户名丢失或没有输入,请重新输入";

        }

        //判断密码不能为空

        else if(pass == null || pass.equals(""))

        {

            errMsg += "您的密码丢失或没有输入,请重新输入";

        }

        //如果用户名和密码不为空,才调用业务逻辑组件

        else

        {

            //vb是业务逻辑组件,通过上面的初始化方法获得

            if (getVb().valid(username,pass))

            {

                return mapping.findForward("welcome");

            }

            else

            {

                errMsg = "您的用户名和密码不匹配";

            }

        }

        //判断是否生成了错误信息

        if (errMsg != null && !errMsg.equals(""))

        {

            //如果有错误信息,将错误信息保存在request里,并跳转到input对应的
            //forward对象

            request.setAttribute("err" , errMsg);

            return mapping.findForward("input");

        }

        else

        {

            //如果没有错误信息,跳转到welcome对应的forward对象

            return mapping.findForward("welcome");

        }

    }

}

在上面的Action代码中,Action显式获取容器中的业务逻辑组件,而不是依靠Spring容器的依赖注入。在这种整合策略下,表现层的控制器组件不再接受IoC容器管理。因此,没有控制器上下文,应将原有的action-servlet.xml文件删除,并修改plug-in元素,不要加载该文件。还要修改Action配置,将Action配置的type元素修改成实际的处理类。这      种整合策略也有一个好处:代码的可读性更强,对传统Struts应用开发的改变很小,容易使用。

将该Action部署在struts-config.xml中,Struts将负责创建该Action。struts-config.xml文件的源代码如下:

<!-- XML文件的版本和编码集 -->

<?xml version="1.0" encoding="gb2312"?>

<!-- Struts配置文件的文件头,包括DTD等信息 -->

<!DOCTYPE struts-config PUBLIC

          "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN"

          "http://struts.apache.org/dtds/struts-config_1_2.dtd">

<!-- struts配置文件的根元素 -->

<struts-config>

    <!-- 配置formbean,所有的formbean都放在form-beans元素里定义 -->

    <form-beans>

        <!-- 定义了一个formbean,确定formbean名和实现类 -->

        <form-bean name="loginForm" type="lee.LoginForm"/>

    </form-beans>

    <!-- 定义action部分,所有的action都放在action-mapping元素里定义 -->

    <action-mappings>

        <!-- 这里只定义了一个action。action的类型为ActionSupport的子类 -->

        <action path="/login" type="type="lee.LoginAction"

            name="loginForm" scope="request" validate="true" input=
            "/login.jsp" >

            <!-- 定义action内的两个局部forward元素 -->

            <forward name="input" path="/login.jsp"/>

            <forward name="welcome" path="/welcome.html"/>

        </action>

    </action-mappings>

    <!-- 加载国际化的资源包 -->

    <message-resources parameter="mess"/>

    <!-- 装载验证的资源文件 -->

    <plug-in className="org.apache.struts.validator.ValidatorPlugIn">

        <set-property property="pathnames" value="/WEB-INF/validator-
        rules.xml,/WEB-INF/validation.xml" />

        <set-property property="stopOnFirstError" value="true"/>

    </plug-in>

</struts-config>

此时,Spring无须使用配置Action的配置文件,这种配置方式非常简单。只需要业务逻辑组件的配置文件,业务逻辑组件的配置文件与前面的示例没有任何改变。

该配置文件中的业务逻辑组件由Spring容器负责实现,而ActionSupport能够先定位Spring容器,然后获得容器的业务逻辑组件。

这种整合策略的执行效果与前面两种整合策略的执行效果完全相同。从代码中分析可见,在这种整合策略下,业务控制器再次退回到Struts起初的设计。仅由strutsconfig.xml中Action充当,从而避免了像DelegatingActionProxy整合策略的性能低下,因为可以只需要创建实际的Action实例。

注意:在这种整合策略下,Struts开发者的改变最小,最接近传统Struts应用开发者的习惯。但这种整合策略会造成代码污染,因为Action类必须继承Spring的ActionSupport类。

posted on 2009-07-19 10:23 jadmin 阅读(...) 评论(...) 编辑 收藏