分析Spring容器启动流程

每当启动Web容器时(例如Tomcat),会读取Web应用中的web.xml文件。以下这段代码就是启动Spring容器的关键代码。在这里插入图片描述
ContextLoaderListener 类继承了ContextLoader,实现 了ServletContextListener接口。

ContextLoader:可以指定在Web应用程序启动时载入Ioc容器,正是通过ContextLoader来实现的。ContextLoader来完成实际创建的WebApplicationContext,也就是Ioc容器的初始化工作,通过的initWebApplicationContext方法来初始化。

ServletContextListener:负责监听ServletContext域的创建和销毁。当域创建时会调用initWebApplicationContext方法,创建WebApplicationContext(web应用上下文对象)并放入ServletContext域中以便调用。



接下来看看initWebApplicationContext方法,是如何进行初始化的。
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
        if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
		// 当容器存在时则提示不能创建,请检查web.xml配置文件是否配置正确
            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!");
        } else {
            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 {
                if (this.context == null) {
					// 如果容器为空,则传入ServletContext对象,获取容器对象
                    this.context = this.createWebApplicationContext(servletContext);
                }

				// 检查创建的ApplicationContext实例是否实现了ConfigurableWebApplicationContext接口
                if (this.context instanceof ConfigurableWebApplicationContext) {
                    ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)this.context;
					
                    if (!cwac.isActive()) {
                        if (cwac.getParent() == null) {
                            ApplicationContext parent = this.loadParentContext(servletContext);
                            cwac.setParent(parent);
                        }

						// 在执行这个方法的时候,会将从ApplicationContext.xml配置文件中获取到的内容配置到已经创建好了的XmlWebApplicationContext容器中去,并调用refresh方法来完成容器的初始化。然后,再将已经完成初始化的XmlWebApplicationContext容器注册到servletContext中去。
                        this.configureAndRefreshWebApplicationContext(cwac, servletContext);
                    }
                }

				// 将初始化的IOC容器对象放入servlerContext域中
                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 var8) {
                logger.error("Context initialization failed", var8);
                servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var8);
                throw var8;
            } catch (Error var9) {
                logger.error("Context initialization failed", var9);
                servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var9);
                throw var9;
            }
        }
    }


可以看到在initWebApplicationContext方法中又调用了createWebApplicationContext方法来创建容器

代码如下

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
		// 决定要创建的ApplicationContext类型
		Class<?> contextClass = this.determineContextClass(sc);
		
		// 如果获取到的ApplicationContext没有实现ConfigurableWebApplicationContext接口则创建失败
		if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
			throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
		} else {
			// 实例化Spring容器
			return (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
		}
    }
此方法的功能:
1. 决定要创建的ApplicationContext类型(通过determineContextClass方法来确定)
2. 实例化一个ApplicationContext



determineContextClass代码如下

	protected Class<?> determineContextClass(ServletContext servletContext) {
		// 获取初始化参数(web.xml中的配置文件)
		String contextClassName = servletContext.getInitParameter("contextClass");
		
		if (contextClassName != null) {// 如果获取到的类名不为空则创建该容器的class对象
			try {
				return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
			} catch (ClassNotFoundException var4) {
				throw new ApplicationContextException("Failed to load custom context class [" + contextClassName + "]", var4);
			}
		} else {
			// 如果没有配置contextClass参数则使用默认的容器对象
			// org.springframwork.web.context.support.XmlWebApplicationCtontext
			// 在这里defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
			contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());

			try {
				return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
			} catch (ClassNotFoundException var5) {
				throw new ApplicationContextException("Failed to load default context class [" + contextClassName + "]", var5);
			}
		}
	}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值