从上一篇可以看出,tomcat在启动项目的时候,通过ContextLoaderListener监听器和DispatcherServlet分别初始化了service层的bean和controller层的bean。

那么通过ContextLoaderListener和DispatcherServlet启动spring有什么区别呢?

ContextLoaderListener监听器在spring-web包内,需要引入spring-web包。DispatcherServlet在spring-webmvc包内,需要引入spring-webmvc包。

<!-- spring使用listener初始化bean(service和dao层bean) -->
    <!-- contextConfigLocation指定上下文文件位置,该项配置为全局配置,
           多个xml文件中间用逗号隔开,此项如果不配置,ContextLoaderListener默认取WEB-INF:applicationContext.xml -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springConfig/spring-beans.xml</param-value>
    </context-param>
    <!-- spring使用listener初始化bean(service和dao层bean) -->
    <listener>
          <listener-class>
              org.springframework.web.context.ContextLoaderListener
          </listener-class>
    </listener>

    <!-- spring使用servlet初始化bean(controller层bean) -->
    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- init-param指定DispatcherServlet上下文配置文件的位置,为局部配置文件,只对DispatcherServlet有用
             如果没有配置init-param,会默认读取/WEB-INF/${servlet-name}-servlet.xml -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springConfig/spring-controllers.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

1、spring原意是跟web不相关的bean,通过ContextLoaderListener初始化;跟web相关的bean,通过DispatcherServlet初始化。

2、ContextLoaderListener默认读取/WEB-INF/applicationContext.xml,可以通过context-param标签指定ContextLoaderListener读取配置文件位置,context-param配置为全局变量。

   DispatcherServlet默认读取/WEB-INF/${servlet-name}-servlet.xml,可以通过标签init-param指定读取配置文件位置,init-param为局部变量,只供DispatcherServlet使用。

3、如果context-param和init-param加载的配置文件出现重复,那么重复的配置文件中定义的bean就会被初始化两次,分别存在两个上下文中。这两个上下文并不是合并互补的,而是互不干扰的。虽然看似所有的bean都被初始化到容器上下文中,但如果配置有问题,还是会有一些bean不能正常工作。

比如context-param文件下里的AOP声明式配置:

<!--aop 行为-->
<bean id="himvn" class="com.tangbao.hellomvn.Himvn" />
<!--aop 注释方式-->
<bean id="hiaspect" class="com.tangbao.hellomvn.Hiaspect" />
<!--aop config-->
<aop:aspectj-autoproxy />
<aop:config>
    <aop:aspect id="aoplianxi" ref="himvn">
        <aop:pointcut id="test1" expression="execution(* com.tangbao.controller.RestlessController.RestlessController(..))"></aop:pointcut>
        <aop:before method="sayHi" pointcut-ref="test1"></aop:before>
        <aop:after method="sayHi" pointcut-ref="test1"></aop:after>
    </aop:aspect>
</aop:config>

假如此aop配置只在ContextLoaderListener中加载,没有在DispatcherServlet 中加载,那么此aop会无效。

所以,在web项目中,可以不使用ContextLoaderListener和全局配置contextConfigLocation参数。把所有spring配置文件统一在DispatcherServlet下配置,所有bean都通过DispatcherServlet初始化,项目可以正常工作。

 

此时,可以通过增加一个总的spring配置xml文件将其他xml文件引入,在web.xml中只添加该配置文件路径即可:

spring初始化warn异常 spring启动初始化_spring

 

 spring-all.xml文件代码为:

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns= "http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd ">
    
    <import resource="/spring-beans.xml" />
    <import resource="/spring-controllers.xml" />

</beans>

web.xml代码为(把contextLoaderListener去掉,DispatcherServlet中只配置spring-all.xml):

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xmlns="http://java.sun.com/xml/ns/javaee" 
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 
        id="WebApp_ID" version="3.0">

    <!-- spring使用listener初始化bean(service和dao层bean) -->
    <!-- contextConfigLocation指定上下文文件位置,该项配置为全局配置,
           多个xml文件中间用逗号隔开,此项如果不配置,ContextLoaderListener默认取WEB-INF:applicationContext.xml -->
    <!-- <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springConfig/spring-beans.xml</param-value>
    </context-param> -->
    <!-- spring使用listener初始化bean(service和dao层bean) -->
    <!-- <listener>
          <listener-class>
              org.springframework.web.context.ContextLoaderListener
          </listener-class>
    </listener> -->

    <!-- spring使用servlet初始化bean(controller层bean) -->
    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- init-param指定DispatcherServlet上下文配置文件的位置,为局部配置文件,只对DispatcherServlet有用
             如果没有配置init-param,会默认读取/WEB-INF/${servlet-name}-servlet.xml -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springConfig/spring-all.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    
  <display-name>Archetype Created Web Application</display-name>
</web-app>

 一个标准的Spring Web项目("Spring MVC Project"模板或者Roo创建的)会在web.xml中包含`ContextLoaderListener`和`DispathcerServlet`.**为什么有些可以只引用`DispatcherServlet`就可以加载所有的配置?**  

我知道 ContextLoaderListener 是应该用在加载与web无关的bean, DispatcherServlet用来加载web相关的bean(Controller相关的等等)。这样就会导致两个 Context:一个父级的和一个孩子级的

背景:  
我一直按照标准的做法做了很多年。  
这经常引起两个context之间的依赖关系问题。在过去我总是可以找个对应的解决办法,并且我很强烈觉得这个机制可以使软件架构更好。但是我现在面临一个问题[problem with the events of the both >contexts](http://stackoverflow.com/questions/8534222/how-to-bridge-spring-application-context-events-to->an-other-context). 

然而这个问题也让我重新思考两个context的模式,我自己问自己:为什么我要把自己带到这个麻烦之中,为什么不把Spring所有的配置放在一个DispatcherServlet文件并且移除掉ContextLoaderListener。(我也会有不同的配置文件,但是只有一个context)。  
有什么理由可以支持移除`ContextLoaderListener`吗?

在你这种情况下,没有理由再去使用ContextLoaderListener和applicationContext.xml配置文件。如果你的app只使用DispatcherServlet就能很好的工作,那么就应该坚持只用这个,这个简单。  
是的,一般推荐的方式是把web不相关的东西放到 webapp级别的 context,但是这除了让有点不方便什么都没有。  
需要强烈使用webapp的理由如下:  
* 如果你有多个需要共享服务的DispatcherServlet
* 如果你有逻辑关系需要访问Spring内部的service
* 如果你有需要hook 在webapp级别的context的Servlet filters

如果上述情形都不适合你,那么你完全不必增加复杂度。  
有一点需要关注的是,当增加后台任务到servlet的context时,比如调度任务、JMS连接等等,如果你忘了添加<load-on-startup>,这些任务将不会第一个起来。