IOC容器的启动过程就是建立上下文的过程,在 http://blog.csdn.net/a18716374124/article/details/78685884 这篇文章中讲解了IoC容器启动最核心的部分,也就是非Web项目启动过程的源码分析。
那么如果是Web项目,启动过程有时怎么样的呢?这篇文章就来看一下,Web项目中IoC容器启动过程,也就是
上下文建立的过程。在web.xml配置文件中,我们配置了ContextLoaderListener,这个类的作用就是在web项目中创建IoC容器,它实现了ServletContextListener接口,提供了与Servlet生命周期结合的回调。WebApplicationContext
创建的过程是在contextInitialized方法中完成的。
接下来我们从Web容器的上下文设计入手,看一下有什么特别之处,然后再去ContextLoaderListener中看整个启动过程。为了方便在web环境中使用IoC容器,Spring提供了WebApplicationContext来满足启动的需要。整个类层次关系如下图:
在WebApplicationContext提供了一个getServletContext方法,用来得到当前web容器的Servlet上下文环境,
相当于得到了一个Web容器级别的全局环境。在Web容器启动过程中,默认使用
XmlWebApplicationContext来实现启动过程,想对的基本的ApplicationContext,增加了对Web环境和
Xml配置定义的处理。看一下XmlWebApplicationContext的源码。通过loadBeanDefinitions方法来
加载bean,
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//创建一个XmlBeanDefinitionReader用来解析BeanDefinition
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 因为XmlWebApplicationContext是DefaultResourceLoader的子类,所以使用它来定位BeanDefinition
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 初始化BeanDefinitionReader
initBeanDefinitionReader(beanDefinitionReader);
// 加载BeanDifinition
loadBeanDefinitions(beanDefinitionReader);
}
进入loadBeanDefinitions方法,最终进入了XmlBeanDefinitionReader中的l
oadBeanDefinitions方法来加载
bean定义信息,最终完成整个上下文初始化过程。
如果想知道再往下是怎么实现的,可以看看
http://blog.csdn.net/a18716374124/article/details/78685884 这篇文章。
理清了Web容器的上下文设计之后,我们进入ContextLoaderListener看一下Web容器启动的具体过程。
启动过程在contextInitialized方法中实现,通过serlvet事件得到servletcontext,
然后进入到ContextLoader的initWebApplicationContext方法,
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
//判断是否存在根上下文,如果存在抛出异常
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present - " +
"check whether you have multiple ContextLoader* definitions in your web.xml!");
}
Log logger = LogFactory.getLog(ContextLoader.class);
servletContext.log("Initializing Spring root WebApplicationContext");
if (logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
}
long startTime = System.currentTimeMillis();
try {
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// 如果有根上下文有父级上下文的话,加载获取父级上下文
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
}
else if (ccl != null) {
currentContextPerThread.put(ccl, this.context);
}
if (logger.isDebugEnabled()) {
logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
}
if (logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
}
return this.context;
}
catch (RuntimeException ex) {
logger.error("Context initialization failed", ex);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex;
}
catch (Error err) {
logger.error("Context initialization failed", err);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
throw err;
}
}
然后在loadParentContext方法中加载父级上下文。下面进入到最重要的
configureAndRefreshWebApplicationContext 方法中,源码如下:
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
if (idParam != null) {
wac.setId(idParam);
}
else {
// Generate default id...
if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
// Servlet <= 2.4: resort to name specified in web.xml, if any.
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getServletContextName()));
}
else {
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
}
wac.setServletContext(sc);
String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (initParameter != null) {
wac.setConfigLocation(initParameter);
}
//定制context
customizeContext(sc, wac);
wac.refresh();
}
refresh方法。再往后的实现过程,可以看看http://blog.csdn.net/a18716374124/article/details/78685884 这篇文章。
以上就是IoC容器在Web容器中的启动过程,与在应用中启动IoC容器过程基本类似,不同之处在于这里需要考虑
Web容器的环境特点,例如各种参数的设置,IoC容器和Web容器的ServletContext相结合。初始化这个根上下文
之后,该上下文会存储到ServletContext中,建立了一个全局的上下文环境。