Spring IoC Context启动过程解析
ServletContext
Web
容器在启动的过程中,会为每个Web
应用程序创建一个对应的ServletContext
对象,它代表了当前的Web
应用,为Spring IoC
容器提供宿主环境。
在部署Web
工程的时候,Web
容器会读取web.xml
,创建ServletContext
,当前Web
工程所有部分都共享这个Context
。context-param
为ServletContext
提供键值对,即Servlet
上下文的信息,这些信息Listener
、Filter
和Servlet
都有可能使用到,因此先加载context-param
,创建ServletContext
,然后加载Listener
,再加载Filter
,最后加载Servlet
。
接下来我将按照这个加载顺序来分析Spring
容器的启动过程。
ContextLoaderListener
web.xml
中配置有ContextLoaderListener
,也可以自定义一个实现了ServletContextListener
接口的Listener
类,web.xml
中的配置实例如下。
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Web
容器在启动的过程中会触发ServletContextEvent
事件,会被ContextLoaderListener
监听到,并调用ContextLoaderListener
中的contextInitialized
方法,contextInitialized
方法如下所示。
public void contextInitialized(ServletContextEvent event) {
this.initWebApplicationContext(event.getServletContext());
}
ContextLoaderListener
类继承了ContextLoader
,在初始化Context
的过程中,调用ContextLoader
的initWebApplicationContext
方法初始化WebApplicationContext
。WebApplicationContext
是一个接口,Spring
默认的实现类为XmlWebApplicationContext
,XmlWebApplicationContext
就是Spring
的IoC
容器。
在初始化XmlWebApplicationContext
之前,Web
容器已经加载了context-param
,web.xml
中的context-param
实例如下所示。作为Spring
的IoC
容器,其对应的Bean
定义的配置正是context-param
指定的。
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
接着进入到initWebApplicationContext
方法内,initWebApplicationContext
方法定义如下(已省略部分代码)。
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
if(servletContext.getAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException("...");
}else{
if(this.context == null) {
this.context = this.createWebApplicationContext(servletContext);
}
}
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
}
在Spring IoC
容器初始化前,initWebApplicationContext
先检测以ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
为key
的值是否为空,若不为空,则初始化IoC Context
,并在初始化完毕后,以ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
为key
将IoC Context
存储到ServletContext
中。
初始化Servlet
Servlet
可以在web.xml
中配置多个,在Spring
中,最基本的Servlet
为DispatcherServlet
,对应的配置实例如下所示。
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/appServlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
DispatcherServlet
会建立自己的IoC Context
,用以持有相关的Bean
,在初始化自己的IoC Context
的过程中,先通过WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
,从ServletContext
中获取WebApplicationContext
,将WebApplicationContext
作为DispatcherServlet的IoC Context
的 parent Context
。DispatcherServlet
自己的IoC Context
的初始化工作在DispatcherServlet
的initStrategies
方法中完成,包括控制器映射,视图解析等,initStrategies
方法如下所示。
protected void initStrategies(ApplicationContext context) {
this.initMultipartResolver(context);
this.initLocaleResolver(context);
this.initThemeResolver(context);
this.initHandlerMappings(context);
this.initHandlerAdapters(context);
this.initHandlerExceptionResolvers(context);
this.initRequestToViewNameTranslator(context);
this.initViewResolvers(context);
this.initFlashMapManager(context);
}
DispatcherServlet
自己的IoC Context
的类型也是XmlWebApplicationContext
,初始化完毕后,Spring
将以与DispatcherServlet
的servlet-name
属性相关的符号作为key
,将IoC Context
保存到 ServletContext
中。这样每个Servlet
就都可以持有自己的Context
,也就是都拥有自己的Bean
空间,同时,各个Servlet
之间还共享着key
为WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
的WebApplicationContext
,其中定义的Bean
为各个Servlet
共享的Bean
。