容器的初始化之 Root WebApplicationContext 容器

首先,我们来回顾一下Tomcat启动context容器的过程。

调用的是StandardContext.startInternal()方法,其中有一段逻辑是初始化Servlet相关的Listener:

在完成 Listener 实例化之后,tomcat 容器便启动 OK 了。此时,tomcat 需要通知应用程序定义的 ServletContextListener,

方便应用程序完成自己的初始化逻辑,它会遍历 ServletContextListener 实例,并调用其 contextInitialized 方法,

比如 spring 的 ContextLoaderListener。

这里,ContextLoaderListener便是Root WebApplicationContext  的初始化和关闭的入口:

实现 ServletContextListener 接口,继承 ContextLoader 类,实现 Servlet 容器启动和关闭时,分别初始化和销毁 WebApplicationContext 容器。

先来看ContextLoaderListenercontextInitialized()方法:

调用的是父类ContextLoader的initWebApplicationContext()方法:

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
		// <1> 若已经存在 ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE 对应的 WebApplicationContext 对象,则抛出 IllegalStateException 异常。
		// 例如,在 web.xml 中存在多个 ContextLoader 。
		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!");
		}

		// <2> 打印日志
		servletContext.log("Initializing Spring root WebApplicationContext");
		Log logger = LogFactory.getLog(ContextLoader.class);
		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) {
				// <3> 初始化 context ,即创建 context 对象
				this.context = createWebApplicationContext(servletContext);
			}

			// <4> 如果是 ConfigurableWebApplicationContext 的子类,如果未刷新,则进行配置和刷新
			if (this.context instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
				// <4.1> 未刷新(激活)
				if (!cwac.isActive()) {
					// The context has not yet been refreshed -> provide services such as
					// setting the parent context, setting the application context id, etc
					// <4.2> 无父容器,则进行加载和设置。
					if (cwac.getParent() == null) {
						// The context instance was injected without an explicit parent ->
						// determine parent for root web application context, if any.
						ApplicationContext parent = loadParentContext(servletContext);
						cwac.setParent(parent);
					}
					// <4.3> 配置 context 对象,并进行刷新
					configureAndRefreshWebApplicationContext(cwac, servletContext);
				}
			}
			// <5> 记录在 servletContext 中,跟<1>中的校验相对应
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

			// <6> 记录到 currentContext 或 currentContextPerThread 中
			ClassLoader ccl = Thread.currentThread().getContextClassLoader();
			if (ccl == ContextLoader.class.getClassLoader()) {
				currentContext = this.context;
			}
			else if (ccl != null) {
				currentContextPerThread.put(ccl, this.context);
			}

			// <7> 打印日志
			if (logger.isInfoEnabled()) {
				long elapsedTime = System.currentTimeMillis() - startTime;
				logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms");
			}

			// <8> 返回 context
			return this.context;
		}
		catch (RuntimeException | Error ex) {
			logger.error("Context initialization failed", ex);
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
			throw ex;
		}
	}

标红的方法1:

初始化context,即创建 WebApplicationContext 对象

看下determineContextClass方法:

  1. 分成两种情况。前者,从 ServletContext 获取配置的 context 类;后者,从 ContextLoader.properties 获取配置的 context 类。
  2. 默认情况下,我们不会主动在 ServletContext 中配置 context 类,所以基本是使用 ContextLoader.properties 配置的 context 类,即 XmlWebApplicationContext 类。

下面是从 ContextLoader.properties 获取配置的 context 类的逻辑:

标红的方法2:

配置 ConfigurableWebApplicationContext 对象,并进行刷新

  1. 此处,注释上既写了 wac ,又写了 context ,实际上,是等价的东西。下面的文字,我们统一用 wac 
  2. <1> 处,如果 wac 使用了默认编号,则重新设置 id 属性。默认情况下,我们不会对 wac 设置编号,所以会执行进去。而实际上,id 的生成规则,也分成使用 contextId 在 <context-param /> 标签中设置,和自动生成两种情况。?? 默认情况下,会走第二种情况
  3. 关键<3> 处,设置 context 的配置文件地址。后面IOC容器初始化要用到。

  1. 【关键】<6> 处, 刷新 wac ,执行初始化。此处,就会进行一些的 Spring 容器的初始化。这个IOC源码解析的时候已经学习过了,就不多说了。

最后我们来看下Root WebApplicationContext 容器的关闭,ContextLoaderListenercontextDestroyed()方法:

以上就是Root WebApplicationContext 容器的初始化和关闭的源码分析。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值