Spring启动流程(备用学习)

Spring启动流程

我们在web服务中是怎么启动Spring容器的,我们通常是在web.xml中配置了一个ContextLoaderListener的监听器,然后设置contextConfigLocation,

1、ContextLoaderListener

这个ContextLoaderListener监听器继承了ContextLoader实现了ServletContextListener

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
    
    public ContextLoaderListener() {
    	}
    
    public ContextLoaderListener(WebApplicationContext context) {
		super(context);
	}
    /**
	 * 开始初始化WebApplicationContext
	 */
	@Override
	public void contextInitialized(ServletContextEvent event) {
		initWebApplicationContext(event.getServletContext());
	}

	/**
	 * 关闭容器
	 */
	@Override
	public void contextDestroyed(ServletContextEvent event) {
		closeWebApplicationContext(event.getServletContext());
		ContextCleanupListener.cleanupAttributes(event.getServletContext());
	}
}

所以当web容器启动后就会被监听到,然后调用contextInitialized方法,进而执行initWebApplicationContext方法。然而这个方法是继承于ContextLoader这个父类,一看这个类的名字就知道这就是一个上下文的加载器。

2、ContextLoader

接下来具体看下initWebApplicationContext方法的实现。而且容器的实际初始化工作是从这里开始的。

总的来说,就是干了下面几件事:
1、初始化之前检查当前ServletContext是否已经存在Spring容器;
2、使用无参构造(Java类)或者主要构造(Kotlin类)实例化1个ConfigurableWebApplicationContext实现类,默认是XMLWebApplicationContext;
3、为已经实例化的ConfigurableWebApplicationContext设置父容器,通常为null,除非自定义子类复写loadParentContext。
4、配置并刷新ConfigurableWebApplicationContext(重点);
5、以WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE为key存储在当前ServletContext中;
6、将ConfigurableWebApplicationContext存储在当前ContextLoader内部。

这里面最重要的方法是configureAndRefreshWebApplicationContext(cwac, servletContext) ,负责为ConfigurableWebApplicationContext设置一些属性为接下来的refresh做准备

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
		/**
		 * 给ConfigurableApplicationContext实现类设置1个unique id
		 */
		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...
				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
						ObjectUtils.getDisplayString(sc.getContextPath()));
			}
		}

		/**
		 * 为当前ConfigurableApplicationContext设置ServletContext
		 */
		wac.setServletContext(sc);
		/**
		 * 获取web.xml中的contextConfigLocation配置信息,通常是spring的xml配置文件地址
		 */
		String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
		if (configLocationParam != null) {
			/**
			 * 为当前ConfigurableApplicationContext设置contextConfigLocation配置信息(xml配置文件)
			 */
			wac.setConfigLocation(configLocationParam);
		}

		ConfigurableEnvironment env = wac.getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
			/**
			 * 初始化PropertySources,一般只要容器调用refresh()方法,就会初始化PropertySources。
			 * 这个地方之所以现在就初始化视为了供refresh()方法执行前的 1、后处理 2、初始化 操作使用
			 */
			((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
		}

		/**
		 * 自定义容器配置,在为容器设置了ConfigLocation之后,refresh之前执行
		 */
		customizeContext(sc, wac);

		/**
		 * 容器刷新,加载配置好的XML、properties或者relational database schema文件
		 * 另外由于这个是容器启动方法,有1点需要注意:如果容器启动失败,所有已创建好的singletons将被销毁。
		 * 此方法有多个实现,但是主体部分是AbstractApplicationContext的refresh()
		 */
		wac.refresh();
	}

可以看到先是为ConfigurableWebApplicationContext设置了一些属性,然后开始refresh容器
1、设置ConfigurableWebApplicationContext的unique id;
2、调用wac.setServletContext(sc),设置ServletContext为当前ServletContext;
3、获取web.xml中contextConfigLocation配置信息,并调用wac.setConfigLocation(configLocationParam)进行设置(重要)
4、提前初始化PropertySources以便在refresh前执行一些后处理或者初始化工作。
5、自定义一些容器配置,设置了ConfigLocation之后,refresh之前执行
6、上面工作完成后,开始refresh容器(很重要!很重要!很重要!)

属性设置完成,要刷新容器了,我们进入AbstractApplicationContext的refresh() 来一探究竟!

3、 AbstractApplicationContext

首先看下refresh()方法实现,这是Spring容器初始化最核心的方法,前面都只是准备工作,不管以哪种方式启动Spring容器,最终必定会调用这个方法。限于篇幅原因,本篇博客只是大致介绍refresh方法各个流程,让大家先有个印象,具体每个流程的细节将会在后续博客陆续阐述。

@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			/**
			 * 刷新前的准备工作:
			 * 1、设置启动日期
			 * 2、激活flag,如closed=false,active=true
			 * 3、初始化PropertySources(处理配置文件中的占位符)并校验必须参数(通过ConfigurablePropertyResolver#setRequiredProperties设置)
			 */
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值