从上一篇可以看出,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-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>,这些任务将不会第一个起来。