SpringIOC容器在Web项目中启动源码分析


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来满足启动的需要。整个类层次关系如下图

SpringIOC容器在Web项目中启动源码分析_第1张图片

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;
		}
	}


创建根上下文的过程在createWebApplicationContext方法中实现,最终通过newInstance方法来实例化根上下文。

然后在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();
	}

此方法中设置了servletContext、ConfigLocation,定制了context。最后进入到了AbstractApplicationContext中的

refresh方法。再往后的实现过程,可以看看http://blog.csdn.net/a18716374124/article/details/78685884 这篇文章。


以上就是IoC容器在Web容器中的启动过程,与在应用中启动IoC容器过程基本类似,不同之处在于这里需要考虑

Web容器的环境特点,例如各种参数的设置,IoC容器和Web容器的ServletContext相结合。初始化这个根上下文

之后,该上下文会存储到ServletContext中,建立了一个全局的上下文环境。


你可能感兴趣的:(源码分析,Spring)