作用:
在启动Web容器时,自动装配Spring applicationContext.xml的配置信息。
ContextLoaderListener是一个监听器,实现了ServletContextListener接口,用来监听Servlet,ServletContextListener是Java EE的标准接口之一,类似tomcat、jetty的java容器启动时便会触发该接口的contextInitialized。
对于一个web容器,web容器提供了一个全局的上下文环境,这个上下文就是ServletContext,为Spring IOC提供宿主环境。
在web容器启动时会触发容器初始化事件,contextLoaderListener监听到这个事件后其contextInitialized方法就会被调用,在这个方法中,spring会初始化一个启动上下文,也就是WebApplicationContext,实际实现类一般是XmlWebApplicationContext,这个其实就是spring的IOC容器,这个容器初始化完后,Spring会将它存储到ServletContext,供后面获取到该IOC容器中的bean。
在contextLoaderListener监听器初始化完毕后,开始初始化web.xml中配置的Servlet,这个servlet可以配置多个,以DispatcherServlet为例,这个servlet实际上是一个标准的前端控制器,用以转发、处理每个servlet请求。DispatcherServlet上下文在初始化时会建立自己的IOC容器,用以持有spring mvc相关的bean。在建立DispatcherServlet自己的IoC上下文时,会利用WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE先从ServletContext中获取之前的根上下文(即WebApplicationContext)作为自己上下文的parent上下文。有了这个parent上下文之后,再初始化自己持有的上下文。这个DispatcherServlet初始化自己上下文的工作在其initStrategies方法中实现的,基本工作就是初始化处理器映射、视图解析等。这个servlet自己持有的上下文默认实现类也是XmlWebApplicationContext。初始化完毕后,spring以与servlet的名字相关的属性为Key,也将其存到ServletContext中。这样每个servlet就持有自己的上下文,即拥有自己独立的bean空间,同时各个servlet共享相同的bean,即根上下文(WebApplicationContext)。
最后讲讲ContextLoaderListener与DispatcherServlet所创建的上下文ApplicationContext的区别:
- ContextLoaderListener中创建ApplicationContext主要用于整个Web应用程序需要共享的一些组件,比如DAO,数据库的ConnectionFactory等。而由DispatcherServlet创建的ApplicationContext主要用于和该Servlet相关的一些组件,比如Controller、ViewResovler等。
- 对于作用范围而言,在DispatcherServlet中可以引用由ContextLoaderListener所创建的ApplicationContext,而反过来不行。
这两个ApplicationContext都是通过ServletContext的setAttribute方法放到ServletContext中的。从web.xml的配置可知ContextLoaderListener会先于DispatcherServlet创建ApplicationContext,DispatcherServlet在创建ApplicationContext时会先找到由ContextLoaderListener所创建的ApplicationContext,再将后者的ApplicationContext作为参数传给DispatcherServlet的ApplicationContext的setParent()方法,作为它的父上下文。
ContextLoaderListener加载过程
1 java容器启动触发ContextLoaderListener的contextInitialized
2 contextInitialized 方法调用ContextLoader的initWebApplicationContext方法。
3 initWebApplicationContext调用createWebApplicationContext方法
4 createWebApplicationContext 调用determineContextClass方法
5 determineContextClass有如下代码
contextClassName = defaultStrategies
.getProperty(WebApplicationContext.class.getName());
显然是从defaultStrategies中加载的
ContextLoader 类中有段静态代码
static {
try {
ClassPathResource resource = new ClassPathResource(
"ContextLoader.properties", ContextLoader.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
} catch (IOException ex) {
throw new IllegalStateException(
"Could not load 'ContextLoader.properties': "
+ ex.getMessage());
}
currentContextPerThread = new ConcurrentHashMap(1);
}
ContextLoader.properties 文件内容如下:
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
至此,determineContextClass方法返回的是XmlWebApplicationContext
6 回到 initWebApplicationContext 方法,调用configureAndRefreshWebApplicationContext方法
7 configureAndRefreshWebApplicationContext 调用了AbstractApplicationContext的refresh方法
8 refresh 方法调用了obtainFreshBeanFactory
9 obtainFreshBeanFactory 调用了AbstractRefreshableApplicationContext类的refreshBeanFactory方法
10 refreshBeanFactory调用了XmlWebApplicationContext的loadBeanDefinitions
11 loadBeanDefinitions中加载了对应的applicationContext.xml