使用Spring插件完成Spring和Struts2的整合

来源:互联网 发布:手机网速测试软件 编辑:程序博客网 时间:2024/05/17 08:59

        最近一直在学习Struts2,所用的学习书是李刚老师著的《Struts2权威指南》, 看后收获很大。在学习过程中,我自己跟着做了一些笔记和例子。下面是关于使用Spring插件完成Spring和Struts2的整合。


       借助于Spring的插件,我们可以非常简单的完成Spring和Struts2的整合,这种整合包括让Action自动装配Spring容器中的Bean,以及让Spring管理应用中的Action两种方式。不管采用那种方式,完成Struts2和Spring的正好都是非常简单的,而且差别不大。

一. 整合Spring前的准备工作
为了在Web应用中使用Spring框架,当然需要将Spring框架的JAR文件复制到Web应用中。正如前面介绍的,通常建议复制dest路径下spring.jar文件,这个文件是Spring框架最全面的类库,使用该文件比使用分模块的类库更加简单。
除此之外,Spring框架还依赖于是commons-logging.jar文件,因此还需将该文件复制到Web应用的WEB-INF/lib路径下。
为了完成Spring和Struts2的整合,还必须安装Struts2的Spring插件。正如前面介绍的,安装Struts2插件是非常简单的,只需要将struts2-spring-plugin-X-X-X.jar文件(文件中的X是版本号)复制到Web应用中即可。
一旦在Web应用中安装了Spring插件,即可充分利用该插件提供的功能:
1.可以通过Spring来创建所有的Action、Interceptor和Result。
2.也可在Struts创建了某个对象(通常是Action实例)之后,Spring将其依赖的组件自动注入该对象。
3.提供了两个拦截器来完成自动装配。
4.除此之外,在使用Spring容器之前,必须先完成Spring容器的初始化,为了完成Spring容器的初始化,Struts2是利用了Spring所提供的两种初始化方式。Spring提供了两种完成初始化的方式:
利用ContextLoaderListener
Spring提供一个ContextLoaderListener类,该类可以作为Web应用的 Listener使用,它会在Web应用启动时自动查找WEB-INF/下的applicationContext.xml配置文件(Spring的配置文件),并且根据该文件来创建Spring容器。
因此,如果Web应用中只有一个Spring配置文件,并且文件名为applicationContext.xml,并将该文件放在Web应用的WEB-INF路径下,则只需在web.xml文件中增加如下一段即可:
<!-- 根据默认配置文件来初始化Spring容器 -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
如果有多个配置文件需要载入,则应该使用<context-param>元素来确定配置文件的文件名。ContextLoaderListener加载时,会查找名为contextConfigLocation的参数。因此,配置context-param时参数名字应该是contextConfigLocation。
因此,如果应用中的Spring配置文件有多个,则应该采用如下形式的web.xml文件来创建Spring容器。
<?xml version="1.0" encoding="GBK"?>
<!-- 指定Web应用配置文件的Schema信息 -->
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <!-- 确定多个配置文件 -->
 <context-param>
  <!-- 参数名为contextConfigLocation -->
  <param-name>contextConfigLocation</param-name>
  <!-- 多个配置文件之间以,隔开 -->
  <param-value>/WEB-INF/daoContext.xml,/WEB-INF/applicationContext.xml</param-value>
 </context-param>
 <!-- 采用Listener完成Spring容器的初始化 -->
 <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
</web-app>
如果没有contextConfigLocation制定配置文件,Spring自动查找WEB-INF路径下applicationContext.xml配置文件;如果有contextConfigLocation,则利用该参数指定的配置文件来创建Spring容器。如果无法找到合适的配置文件,Spring将无法正常初始化。

采用load-on-startup Servlet创建ApplicationContext
利用Listener创建Spring容器简单,易用,但有一个明显的局限:因为Listener是Servlet 2.3以后才开始出现的规范,这意味着如果Web服务器不支持Servlet 2.3以上的规范,将无法利用Listener来创建Spring容器。
已经知道,如下Web服务器不支持Servlet 2.3以上的规范:
1.BEA WebLogic up to 8.1 SP2或者更低版本。
2.IBM WebSphere 5.x或者更低版本。
3.Oracle OC4J 9.0.3或者更低版本。
此时,只能利用Web应用中的load-on-startup的Servlet。
提示:load-on-startup Servlet的作用与Listener的作用大致相同,区别是Listener是后来的规范,Listener的出现,取代了原来load-on-startup Servlet的作用。
为了使用load-on-startup Servlet来创建Spring容器,Spring提供了一个特殊的Servlet类:ContextLoaderServlet。该Servlet在初始化时,会自动查找WEB-INF/下的applicationContext.xml文件。
如果只有一个配置文件,并且文件名为:applicationContext.xml。在web.xml文件中增加如下一段即可:
<servlet>
 <servlet-name>context</servlet-name>
 <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
 <load-on-startup>1</load-on-startup>
</servlet>
配置该Servlet时,配置了一个load-on-startup子元素,该元素指定该Servlet会随Web应用的启动而完成初始化。load-on-startup元素的值设置启动级别,该级别越小,该Servlet越早完成初始化。因此上面配置文件将load-on-startup级别设置为1。但不管该值多小,Listener都比该Servlet更早启动。
该Servlet仅用于提供“后台”服务,负责创建Spring 容器,无需响应客户请求,因此无需配置servlet-mapping。
如果有多个配置文件,一样使用<context-param>元素来确定多个配置文件。事实上,不管是ContextLoaderServlet,还是ContextLoaderListener,都是通过调用ContextLoader来创建Spring容器的。在ContextLoader代码的第240行,有如下代码:
String configLocation = servletContext.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocation != null) {
 wac.setConfigLocations(StringUtils.tokenizeToStringArray(configLocation,
 ConfigurableWebApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
其中CONFIG_LOCATION_PARAM是该类的常量,其值为contextConfigLocation。可看出:ContextLoader首先检查servletContext中是否有contextConfigLocation的参数,如果有该参数,则加载该参数指定的配置文件。加载该参数后,将该参数以指定的符号分解成字符串数组,每个字符串元素指定一个配置文件。
如果Web应用中包含了多个Spring配置文件,则系统所用的web.xml配置文件的代码如下:
<?xml version="1.0" encoding="GBK"?>
<!-- 指定Web应用配置文件的Schema信息 -->
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <!-- 确定多个配置文件 -->
 <context-param>
  <!-- 参数名为contextConfigLocation -->
  <param-name>contextConfigLocation</param-name>
  <!-- 多个配置文件之间以,隔开 -->
  <param-value>/WEB-INF/daoContext.xml,/WEB-INF/applicationContext.xml</param-value>
 </context-param>
 <!-- 采用load-on-startup Servlet创建Spring容器 -->
<servlet>
  <servlet-name>context</servlet-name>
  <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
  <!-- 下面值小一点比较合适,值越小,越早进行Spring容器的创建 -->
  <load-on-startup>1</load-on-startup>
</servlet>
</web-app>
因为ContextLoaderServlet与ContextLoaderListener底层都依赖于ContextLoader。因此,二者的效果几乎没有区别。他们之间细微区别不是它们本身引起的,而是由于Servlet规范产生的:Listener总比所有的Servlet更优先加载。因此,采用ContextLoaderListener创建ApplicationContext的时机更早。

二. 整合Spring的思考
对于一个基于B/S架构的Java EE应用而言,用户请求总是向MVC框架的控制器请求,而当控制器拦截到用户请求后,必须调用业务逻辑组件来处理用户请求。此时有一个问题:控制器应该如何获得业务逻辑组件?
最容易想到的策略是,直接通过new关键字创建业务逻辑组件,然后调用业务逻辑组件的方法,根据业务逻辑方法的返回值确定结果。
实际的应用中,很少见到采用上面的访问策略,因为这是一种非常差的策略。不这样做至少有如下三个理由:
1.控制器直接创建业务逻辑组件,导致控制器和业务逻辑组件的耦合降低到代码层次,不利于高层次解耦。
2.控制器不应该负责业务逻辑组件的创建,控制器只是业务逻辑组件的使用者。无需关心业务逻辑组件的实现。
3.每次创建新的业务逻辑组件导致性能下降。
为了避免这种情况,实际开发中采用工厂模式来取得业务逻辑组件;或者采用服务定位器模式,对于采用服务定位器的模式,是远程访问的场景,在这种场景下,业务逻辑组件已经在某个容器中运行,并对外提供某种服务。控制器无需理会该业务逻辑组件的创建,直接调用即可,但在调用之前,必须先找到该服务——这就是服务定位器的概念,传统以EJB为基础的Java EE应用通常采用这种结构。
对于轻量级的Java EE应用,工厂模式则是更实际的策略。因为轻量级的Java EE应用里,业务逻辑组件不是EJB,通常就是一个POJO,业务逻辑组件的生成通常由工厂负责,而且工厂可以保证该组件的实例只需一个就够了,可以避免重复实例化造成的系统开销浪费。
        采用工厂模式,将控制器与业务逻辑组件的实现分离。从而提供更好的解耦,
在采用工厂模式的访问策略中,所有的业务逻辑组件的创建由工厂负责,业务逻辑组件的运行也由工厂负责。而控制器只需定位工厂实例即可。
如果系统采用Spring框架,则Spring成为最大的工厂。Spring负责业务逻辑组件的创建和生成,并可管理业务逻辑组件的生命周期。可以如此理解:Spring是个性能非常优秀的工厂,可以生产出所有的实例,从业务逻辑组件,到持久层组件,甚至控制器。
现在的问题是:控制器如何访问到Spring容器中的业务逻辑组件?为了让Action访问Spring的业务逻辑组件,有两种策略:
1.Spring管理控制器,并利用依赖注入为控制器注入业务逻辑组件。
2.控制器定位Spring工厂,也就是Spring的容器,从Spring容器中取得所需的业务逻辑组件。
对于这两种策略,Struts2都提供了对应的整合实现

原创粉丝点击