ContextLoaderListener和DispatcherServlet都会在Web容器启动的时候加载一下bean配置. 区别在于:
- DispatcherServlet一般会加载MVC相关的bean配置管理(如: ViewResolver, Controller, MultipartResolver, ExceptionHandler, etc.)
- ContextLoaderListener一般会加载整个Spring容器相关的bean配置管理(如: Log, Service, Dao, PropertiesLoader, DataSource Bean, etc.)
DispatcherServlet默认使用WebApplicationContext作为父上下文,如果在自己context中找不到对应的bean,则会在父上下文(也就是ApplicationContext)中去找。这也解释了为什么我们可以在DispatcherServlet中获取到由ContextLoaderListener对应的ApplicationContext中的bean.
DispatcherServlet也可以配置自己的初始化参数,覆盖默认配置:
参数 | 描述 |
contextClass | 实现WebApplicationContext接口的类,当前的servlet用它来创建上下文。如果这个参数没有指定, 默认使用XmlWebApplicationContext。 |
contextConfigLocation | 传给上下文实例(由contextClass指定)的字符串,用来指定上下文的位置。这个字符串可以被分成多个字符串(使用逗号作为分隔符) 来支持多个上下文(在多上下文的情况下,如果同一个bean被定义两次,后面一个优先)。 |
namespace | WebApplicationContext命名空间。默认值是[server-name]-servlet。
|
如下:
[xml] view plain copy
- <servlet>
- <servlet-name>demo</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <load-on-startup>1</load-on-startup>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>classpath:spring-servlet-config.xml</param-value>
- </init-param>
- </servlet>
- <servlet-mapping>
- <servlet-name>demo</servlet-name>
- <url-pattern>/</url-pattern>
- </servlet-mapping>
<servlet> <servlet-name>demo</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-servlet-config.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>demo</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
值得注意的是, DispatcherServlet的上下文仅仅是Spring MVC的上下文, 而ContextLoaderListener的上下文则对整个Spring都有效. 一般Spring web项目中同时会使用这两种上下文.
上下文创建完后会放在ServletContext对象中, 其中:
1) ContextLoaderListener加载的上下文放在ServletContext的key为WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE属性中;
2) DispatcherServlet加载的上下文在每次请求时会放一份在request对象的key为WEB_APPLICATION_CONTEXT_ATTRIBUTE属性中.
因而两者的获取方式也不一样, 前者可以通过:
WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext)或
WebApplicationContextUtils.getWebApplicationContext(servletContext)或
WebApplicationContextUtils.getWebApplicationContext(servletContext,attrname)方法来获取对应的applicationContext,
而后者则通过:
RequestContextUtils.getWebApplicationContext(request)或
WebApplicationContextUtils.getWebApplicationContext(servletContext,attrname)方法来获取对应的applicationContext.
(注: 对于ContextLoaderListener加载的上下文, attrname即上面提到的WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;
而对于DispatcherServlet中的上下文则为FrameworkServlet.class.getName() + “.CONTEXT.” + getServletName())
通过上下文所在的属性可以看出,如果通过WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext)来试图获取DispatcherServlet加载的applicationContext时, 就会抛出”No WebApplicationContext found: no ContextLoaderListener registered?”的异常.
注:本文部分转载自网络, 其中大多数参考了ContextLoaderListener和Spring MVC中的DispatcherServlet学习
父子两个WebApplicationContext带来的麻烦
父WebApplicationContext里的bean可以在不同的子WebApplicationContext里共享,而不同的子WebApplicationContext里的bean区不干扰。
但是实际上有会不少的问题:
如果开发者不知道Spring mvc里分有两个WebApplicationContext,导致各种重复构造bean,各种bean无法注入的问题。
有一些bean,比如全局的aop处理的类,如果先父WebApplicationContext里初始化了,那么子WebApplicationContext里的初始化的bean就没有处理到。如果在子WebApplicationContext里初始化,在父WebApplicationContext里的类就没有办法注入了。
细节:
1. 从DispatcherServlet和ContextLoaderListener的初始化过程可以看出,二者分别会生成一个WebApplicationContext,且以不同的attrName注册到web容器中
2. 根据web.xml的加载顺序,listener总是先于servlet进行加载,因此虽然DispatcherServlet和ContextLoaderListener的WebApplicationContext不同,但是ContextLoaderListener的WebApplicationContext总是DispatcherServlet的父ApplicationContext
3. 同一个web容器中,只允许存在一个ContextLoaderListener,但可以存在多个DispatcherServlet
4. 由于二者生成的WebApplicationContext不同,因而这两个WebApplicationContext会分别去加载它们的配置,生成不同的BeanFactory;获取Spring Bean时,会先从DispatcherServlet的WebApplicationContext中查找,若不存在再通过父ApplicationContext,即ContextLoaderListener的WebApplicationContext,进行查找
5. 若二者的配置文件对Bean的定义存在交叉(即二者的配置文件中都定义了相同class且相同beanName的bean),则两个WebApplicationContext中都会保存一份该bean,但实际调用中只会用到DispatcherServlet中的bean,ContextLoaderListener中的bean无法调用到,成为内存泄漏
6. DispatcherServlet除了与ContextLoaderListener一样,会加载用户配置的bean以外,还会自动加载与web mvc相关的spring bean,如RequestMapping、ViewResolver、ExceptionHandler等
7. 项目中使用spring框架有2种方式:
1)listener下的ContextLoaderListener 是一种引入方式,默认读取/WEB-INF/applicationContext.xml
2)若是spring-web项目,DispatcherServlet 也是一种引入方式,默认读取/WEB-INF/${servlet-name}-servlet.xml