一.写在前面
上次的ServletContext与WebApplicationContext一文只是梳理清楚ServletContext和由ContextLoaderListener加载的WebApplicationContext的关系,今天再来梳理一下
ContextLoaderListener加载的WebApplicationContext和DispatcherServlet加载的applicationContext是spring MVC的上下文的关系
二.二者关系
1.ContextLoaderListener所加载的context
被spring通过servletContext.setAttribute(WebApplicationContext.
ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context)
存放到ServletContext的attribute中。
该上下文可通过
WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext)或WebApplicationContextUtils.getWebApplicationContext(servletContext)
方法来获取
2.DispatcherServlet加载context完成后,如果publishContext属性的值设置为true的话(缺省为true)
会将context存放在ServletContext的key为org.springframework.web.servlet.
FrameworkServlet.CONTEXT. + (servletName)的attribute中。
xml配置如下:
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:dispatcher-servlet.xml</param-value>
</init-param>
</servlet>
DispatcherServlet所加载的applicationContext可以认为是mvc私有的context,
由于保存在servletContext中的key值与通过ContextLoaderListener加载进来的applicationContext使用的key值不相同,
因此如果只使用DispatcherServlet加载context的话,如果程序中有地方使用WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext)
来试图获取applicationContext时,就会抛出”No WebApplicationContext found: no ContextLoaderListener registered?”的exception。
三.注意
Spring的ContextLoaderListener所创建出来的context和Spring MVC DispatcherServlet所创建出来的context
是父子关系,FrameworkServlet在实例化对应的applicationContext后通过setParent
将从ServletContext中获取到的ContextLoaderListener创建的applicaitonContext设置成父上下文,
然后加载在对应的xml配置文件对其初始化。
father WebApplicationContext里的bean可以被注入到child WebApplicationContext里的bean,
而child WebApplicationContext的bean则不能被注入到parent WebApplicationContext里的bean。
所以在使用Spring MVC时启用自动检测功能,应在applicationContext.xml里只component-scan非Controller的类,
而在Spring MVC里只component-scan Controller类
applicationContext.xml配置如下
<context:component-scan base-package="com.test">
<context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation" />
<context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice" />
</context:component-scan>
dispatcher-servlet.xml的配置如下
<context:component-scan base-package="com.test.web" use-default-filters="false">
<context:include-filter expression="org.springframework.stereotype.Controller"
type="annotation" />
<context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice" />
</context:component-scan>
虽然这两个context上下文是一对父子关系,但它们加载的bean不是合并存储的,所以个人建议,
基于mvc相关的spring配置由DispatcherServlet加载,而其余的JavaBean都交给ContextLoaderListener加载