Spring源码学习系列二:IOC容器启动流程

IOC容器启动流程

  • 从Spring IOC说起
    • web.xml
      • 1、ContextLoaderListener
      • 2、ContextLoader
      • 3、 AbstractApplicationContext
        • 3.1 prepareRefresh()
        • 3.2 obtainFreshBeanFactory()
          • 3.2.1 AbstractRefreshableApplicationContext#refreshBeanFactory()
        • 3.3 prepareBeanFactory(beanFactory)
        • 3.4 postProcessBeanFactory(beanFactory)
          • 3.4.1 AbstractRefreshableWebApplicationContext#postProcessBeanFactory
        • 3.5 invokeBeanFactoryPostProcessors(beanFactory)
        • 3.6 registerBeanPostProcessors(beanFactory)
        • 3.7 initMessageSource()
        • 3.8 initApplicationEventMulticaster()
        • 3.9 onRefresh()
        • 3.10 registerListeners()
        • 3.11 finishBeanFactoryInitialization(beanFactory)
        • 3.12 finishRefresh()
        • 3.13 destroyBeans() & cancelRefresh(ex)
        • 3.14 resetCommonCaches()

从Spring IOC说起

众所周知,Spring有2大核心,IOC和AOP,而IOC又是AOP的基础,因此Spring源码学习系列的第一篇博客以IOC开始。本篇博客以IOC容器的启动流程为切入点,介绍从web容器启动,Spring ContextLoaderListener监听到ServletContextEvent并初始化WebApplicationContext到最终WebApplicationContext初始化完毕存入ServletContext的大致流程。

web.xml

Spring容器有多种启动方式,这里以常见web.xml配置为例,介绍web容器是如何启动Spring容器的。

在web.xml中,通常能看到下面配置
Spring源码学习系列二:IOC容器启动流程_第1张图片

这里干了2件事:
1、配置了一个监听器ContextLoaderListener。
2、设置参数contextConfigLocation,值为类路径下所有applicationContext*.xml文件。

接下来我们看下这个ContextLoaderListener是怎么工作的。

1、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(ServletContextEvent event) 监听到,进而执行ContextLoader#initWebApplicationContext(event.getServletContext()) 方法。

2、ContextLoader

看名字就知道这是一个上下文加载器,容器的实际初始化工作是从这里开始的。我们看下initWebApplicationContext方法实现

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
		/**
		 * 检查servletContext中是否已经存放有1个root application,如果有,那么抛出IllegalStateException。
		 * 比如在web.xml中配置多个ContextLoader,导致启动ServletContext时初始化了多个WebApplicationContext。
		 * 正常情况下,当WebApplicationContext初始化完毕后会作为属性存储在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!");
		}

		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) {
				/**
				 * 使用无参构造(Java类)或者主要构造(Kotlin类)实例化1个ConfigurableWebApplicationContext实现类,
				 * 默认是XMLWebApplicationContext。
				 * ps:为什么默认是XMLWebApplicationContext?
				 * 参见:ContextLoader的static代码块,初始化defaultStrategies,会加载ContextLoader.properties
				 * org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
				 *
				 * Spring中大量用到了了defaultStrategies,定义在1个properties文件中,然后在static代码块加载这个配置文件,缓存里面的配置。
				 * 比如DispatcherServlet会使用DispatcherServlet.properties作为默认策略,该配置文件集中定义了Spring Mvc中的默认组件,如
				 * LocaleResolver、ThemeResolver、HandlerMapping、HandlerAdapter、HandlerExceptionResolver、ViewResolver等
				 */
				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) {
						// The context instance was injected without an explicit parent ->
						// determine parent for root web application context, if any.
						/**
						 * 获取父容器并进行设置,loadParentContext(ServletContext servletContext)是个模板方法,
						 * 默认返回null,如果需要定制可以使用子类继承并进行复写
						 */
						ApplicationContext parent = loadParentContext(servletContext);
						/**
						 * parent默认是null
						 */
						cwac.setParent(parent);
					}
					/**
					 * 最最重要的步骤:配置并刷新WebApplicationContext!!!
					 */
					configureAndRefreshWebApplicationContext(cwac, servletContext);
				}
			}
			/**
			 * WebApplicationContext初始化完成,存入当前servletContext,非常重要,正是因为这一点,可以通过下面方式获取WebApplicationContext
			 * 1、WebApplicationContextUtils.getRequiredWebApplicationContext(ServletContext sc)
			 * 2、WebApplicationContextUtils.getWebApplicationContext(ServletContext sc)
			 * 区别:1获取失败抛出异常,2返回null
			 */
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

			ClassLoader ccl = Thread.currentThread().getContextClassLoader();
			/**
			 * 将初始化完成的WebApplicationContext存入当前ContextLoader,可通过下面方式获取WebApplicationContext
			 * ContextLoader.getCurrentWebApplicationContext()
			 */
			if (ccl == ContextLoader.class.getClassLoader()) {
				currentContext = this.context;
			}
			else if (ccl != null) {
				currentContextPerThread.put(ccl, this.context);
			}

			if (logger.isInfoEnabled()) {
				long elapsedTime = System.currentTimeMillis() - startTime;
				logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms");
			}

			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、初始化之前检查当前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();

			/**
			 * 刷新并返回1个BeanFactory,概括起来干了下面几件事:
			 * 1、判断是否已经有了BeanFactory,如果有了就销毁所单例bean,并关闭BeanFactory。
			 * 2、初始化1个BeanFactory,设置允许BeanDefinition覆盖以及循环引用
			 * 3、根据之前设置好的ConfigLocation定位配置文件,加载并解析形成beanName-beanDefinition形式的map并存储在
			 * BeanFactory中。
			 * ps:在ContextLoader中wac.setConfigLocation(configLocationParam);
			 * 接口方法在ConfigurableWebApplicationContext中setConfigLocation(String configLocation)
			 * 4、将设置好的beanFactory存入ApplicationContext内部
			 */
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			/**
			 * 为beanFactory做些准备工作
			 * 1、设置beanFactory的ClassLoader
			 * 2、添加3个BeanPostProcessor
			 * (1)ApplicationContextAwareProcessor---在bean初始化前回调bean实现的相关Aware接口方法进行依赖注入
			 * (2)ApplicationListenerDetector---在bean初始化后检查bean如果实现ApplicationListener接口,就注册为ApplicationListener存入set中
			 * (3)LoadTimeWeaverAwareProcessor---AspectJ相关,在bean初始化后检查bean如果实现LoadTimeWeaverAware接口,就调用setLoadTimeWeaver进行注入
			 * 3、忽略和注册bean的一些特殊依赖
			 * (1)忽略一些Aware接口实现类依赖
			 * (EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware、ApplicationEventPublisherAware
			 * 、MessageSourceAware、ApplicationContextAware)
			 * (2)注册当前ApplicationContext本身或者内部beanFactory作为某些特殊依赖
			 * (BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext)
			 * 4、对于一些特殊bean(environment、systemProperties、systemEnvironment),如果用户没有注册,那么Spring将自动注册
			 */
			prepareBeanFactory(beanFactory);

			try {
				/**
				 * 第一个扩展点:postProcessBeanFactory(beanFactory)是一个模板方法,本身并没有实现。
				 * 前面的工作已经添加了一些通用的BeanPostProcessor
				 * 用户如果继承ApplicationContext实现自己的子类,那么可以在这个地方添加一些自已感兴趣的BeanPostProcessor
				 */
				postProcessBeanFactory(beanFactory);

				/**
				 * 执行所有BeanFactoryPostProcessor实现类的回调方法,必须在
				 * 任何单例bean实例化之前执行
				 */
				invokeBeanFactoryPostProcessors(beanFactory);

				/**
				 * 注册前面添加的所有BeanPostProcessor实现类,必须在所有容器bean实例化之前注册
				 * 注意:这里只是注册,还没有执行回调方法,这些回调方法会在bean初始化前后执行
				 */
				registerBeanPostProcessors(beanFactory);

				/**
				 * 初始化当前容器的MessageSource,国际化相关,非重点
				 */
				initMessageSource();

				/**
				 * 初始化当前容器的ApplicationEventMulticaster
				 */
				initApplicationEventMulticaster();

				/**
				 * 第二个扩展点:onRefresh()是1个模板方法。在context子类中初始化一些其他特殊的bean(在单例bean初始化之前执行)
				 */
				onRefresh();

				/**
				 * 注册ApplicationListener实现类,即事件监听器
				 */
				registerListeners();
				
				/**
				 * 实例化所有非懒加载的单例bean
				 * 在上面的ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()中,
				 * 只是注册了beanDefinition,还没有真正实例化,在这里会进行真正的实例化
				 */
				finishBeanFactoryInitialization(beanFactory);

				/**
				 * 到这里,容器已经初始化完毕,会发布ContextRefreshedEvent。
				 * 传统的容器启动后执行初始化方法有3种方式:
				 * 1、在bean标签中指定init-method,或者@Bean(initMethod=???)
				 * 2、实现InitializingBean接口
				 * 3、使用@PostConstruct
				 *
				 * 这里我们可以利用这个事件实现第4种方式
				 * 比如实现ApplicationListener接口,当监听到ContextRefreshedEvent事件时,方法onApplicationEvent(event)
				 * 将被调用,然后做一些初始化工作!
				 */
				finishRefresh();
			}

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

				/**
				 * 如果容器刷新异常,销毁所有已经创建的单例bean
				 */
				destroyBeans();

				/**
				 * 设置active=false,表示容器没有刷新过,允许下一次刷新
				 */
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {

				/**
				 * 重置缓存数据
				 */
				resetCommonCaches();
			}
		}
	}

上面的代码片段和注释描述了容器refresh的所有流程,下面简单看下这些流程

3.1 prepareRefresh()

这个流程主要是刷新前的准备工作

protected void prepareRefresh() {
		// Switch to active.
		this.startupDate = System.currentTimeMillis();
		//设置容器开启
		this.closed.set(false);
		//设置容器已激活,如果容器初始化异常会重置active为false,表示未激活,下次可以重新refresh
		this.active.set(true);

		if (logger.isDebugEnabled()) {
			if (logger.isTraceEnabled()) {
				logger.trace("Refreshing " + this);
			}
			else {
				logger.debug("Refreshing " + getDisplayName());
			}
		}

		//模板方法,初始化PropertySource处理配置文件中的占位符
		initPropertySources();

		//校验设置为required的参数,在ConfigurablePropertyResolver#setRequiredProperties中设置
		getEnvironment().validateRequiredProperties();

		//初始化earlyApplicationListeners存储预刷新的ApplicationListener
		if (this.earlyApplicationListeners == null) {
			this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
		}
		else {
			// Reset local application listeners to pre-refresh state.
			this.applicationListeners.clear();
			this.applicationListeners.addAll(this.earlyApplicationListeners);
		}

		//初始化earlyApplicationEvents存储早期ApplicationEvent,一旦multicaster可用便会发布这些事件
		this.earlyApplicationEvents = new LinkedHashSet<>();
	}

3.2 obtainFreshBeanFactory()

这个方法很重要,内部有1个模板方法refreshBeanFactory(),负责初始化1个DefaultListableBeanFactory,并维护在ApplicationContext内部,以后ApplicationContext有关BeanFactory的工作都由这个DefaultListableBeanFactory去完成!

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		/**
		 * 刷新BeanFactory,这是1个模板方法,需要子类去实现(Spring用到了大量模板+钩子设计模式)
		 * 此处我们以AbstractRefreshableApplicationContext实现为例
		 */
		refreshBeanFactory();
		//返回刷新好的BeanFactory
		return getBeanFactory();
	}

refreshBeanFactory()是一个模板方法,由子类负责实现。这里我们以AbstractRefreshableApplicationContext为例介绍下这个实现。

3.2.1 AbstractRefreshableApplicationContext#refreshBeanFactory()
@Override
	protected final void refreshBeanFactory() throws BeansException {
		/**
		 * 判断是否已经有了BeanFactory
		 */
		if (hasBeanFactory()) {
			/**
			 * 如果已经有了:
			 * 1、销毁所有单例bean
			 * 2、关闭BeanFactory
			 */
			destroyBeans();
			closeBeanFactory();
		}
		try {
			/**
			 * 创建1个DefaultListableBeanFactory
			 */
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());

			/**
			 * 设置2个属性:非常重要,尤其是第2个属性
			 * 1、allowBeanDefinitionOverriding:是否允许BeanDefinition覆盖(默认true,即对于name相同的bean,后面的将覆盖前面的)
			 * 2、allowCircularReferences:是否允许循环引用(默认true)
			 */
			customizeBeanFactory(beanFactory);
			/**
			 * 加载beanDefinition到beanFactory:注意是beanDefinition(bean的原始数据),不是bean,此时bean尚未实例化
			 * 此方法有多个实现,对于XmlWebApplicationContext来说,就是加载web.xml中contextConfigLocation配置的spring xml配置文件
			 * 如spring-mvc.xml,spring-jdbc.xml,applicationContext.xml等,解析完成后以beanName-beanDefinition的k-v形式缓存在
			 * beanFactory中
			 */
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {

				/**很重要:
				 * 将设置好的beanFactory存入ApplicationContext内部,
				 * 以后ApplicationContext中与beanFactory相关的操作都由这个beanFactory来完成
				 */
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}

有2个细节需要特别注意:
1、customizeBeanFactory(beanFactory),设置2个重要属性,尤其是第2个
(1)allowBeanDefinitionOverriding:是否允许BeanDefinition覆盖(默认true,即对于name相同的bean,后面的将覆盖前面的)
(2)allowCircularReferences:是否允许循环引用(默认true)
2、loadBeanDefinitions(beanFactory),加载配置文件并解析,注册BeanDefinition到BeanFactory中,以便后面实例化bean。

3.3 prepareBeanFactory(beanFactory)

在3.2 中已经初始化了1个BeanFactory并维护在ApplicationContext内部,在设置了2个重要属性和加载配置文件、解析并注册BeanDefinition后,这一步需要继续为这个BeanFactory做些准备工作,主要是以下内容:

1、设置beanFactory的ClassLoader为当前当前ApplicationContext的类加载器
2、注册3个BeanPostProcessor
(1)ApplicationContextAwareProcessor—在bean初始化前回调bean实现的相关Aware接口方法进行依赖注入
(2)ApplicationListenerDetector—在bean初始化后检查bean如果实现ApplicationListener接口,就注册为ApplicationListener存入set中
(3)LoadTimeWeaverAwareProcessor—AspectJ相关,在bean初始化后检查bean如果实现LoadTimeWeaverAware接口,就调用setLoadTimeWeaver进行注入
3、忽略和注册bean的一些特殊依赖
(1)忽略一些Aware接口实现类依赖
(EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware)
(2)注册当前ApplicationContext本身或者内部beanFactory作为某些特殊依赖
(BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext)
4、自动注册一些特殊bean:对于一些特殊bean(environment、systemProperties、systemEnvironment),如果用户没有注册,那么Spring将自动注册

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		/**
		 * 设置内部beanFactory的ClassLoader为加载当前ApplicationContext的类加载器
		 */
		beanFactory.setBeanClassLoader(getClassLoader());
		beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
		beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

		// Configure the bean factory with context callbacks.
		/**
		 * 注册1个BeanPostProcessor---ApplicationContextAwareProcessor,极为重要的1个BeanPostProcessor,会在bean
		 * 初始化之前检查bean是否实现了某些Aware接口并回调相应的set方法进行装配
		 */
		beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
		/**
		 * 如果bean依赖于以下接口的实现类,那么在自动装配时将忽略掉
		 * ps:那Spring通过什么方式进行装配?
		 * Spring会通过上面注册的ApplicationContextAwareProcessor进行装配,该接口的postProcessBeforeInitialization方法会在
		 * 每一个bean初始化之前依次检查bean是否实现下面接口:
		 * 1、EnvironmentAware
		 * 2、EmbeddedValueResolverAware
		 * 3、ResourceLoaderAware
		 * 4、ApplicationEventPublisherAware
		 * 5、MessageSourceAware
		 * 6、ApplicationContextAware
		 * 如果实现了将调用相应的set方法进行设置!
		 */
		beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
		beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
		beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
		beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
		beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
		beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

		// BeanFactory interface not registered as resolvable type in a plain factory.
		// MessageSource registered (and found for autowiring) as a bean.
		/**
		 * 如果bean依赖了下面几个接口的实现类,那么注入相应的值:
		 * 1、由于ApplicationContext本身继承了ResourceLoader,ApplicationEventPublisher,ApplicationContext,因此直接注入当前
		 * ApplicationContext即可
		 * 2、由于ApplicationContext内部维护了1个初始化完成的BeanFactory,因此直接注入ApplicationContext内部beanFactory
		 * 3、之所以没有MessageSource,是因为MessageSource被注册成为1个普通的bean
		 */
		beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
		beanFactory.registerResolvableDependency(ResourceLoader.class, this);
		beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
		beanFactory.registerResolvableDependency(ApplicationContext.class, this);

		// Register early post-processor for detecting inner beans as ApplicationListeners.
		/**
		 * 注册BeanPostProcessor---ApplicationListenerDetector,用于在bean初始化完成后检查该bean
		 * 是否实现ApplicationListener接口,如果是,那么将其注册为ApplicationListener(事件监听器)
		 * AbstractApplicationContext内部维护了1个Set>
		 */
		beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

		// Detect a LoadTimeWeaver and prepare for weaving, if found.
		/**
		 * 如果注册了名为loadTimeWeaver的bean到beanFactory中,
		 * 那么注册BeanPostProcessor---LoadTimeWeaverAwareProcessor。
		 * 这个bean很特殊,涉及到AspectJ,在运行期内进行织入,与Spring AOP 不一样。
		 *
		 * LoadTimeWeaverAwareProcessor会在bean初始化完成后检查bean是否
		 * 实现了LoadTimeWeaverAware接口,如果是,从beanFactory中获取loadTimeWeaver,
		 * 并调用bean.setLoadTimeWeaver(loadTimeWeaver)进行注入
		 *
		 */
		if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
			beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
			// Set a temporary ClassLoader for type matching.
			beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
		}

		// Register default environment beans.
		/**
		 * 如果beanFactory未注册environment,此处自动注册1个
		 */
		if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
			beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
		}
		/**
		 * 如果beanFactory未注册systemProperties,此处自动注册1个
		 */
		if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
			beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
		}
		/**
		 * 如果beanFactory未注册systemEnvironment,此处自动注册1个
		 */
		if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
			beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
		}
	}

3.4 postProcessBeanFactory(beanFactory)

在3.3中,Spring为我们自动注册了几个BeanPostProcessor,如果还是无法满足我们的需求,那么我们可以在这一步自己手动注册一些感兴趣的BeanPostProcessor!

	/**
	 * 在ConfigurableListableBeanFactory初始化完成后做一些改动(此时所有definition都已注册但尚未实例化)。
	 * 我们可以在自定义ApplicationContext实现类中重写这个方法并添加一些特殊的BeanPostProcessor
	 * @param beanFactory
	 */
	protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
	}

Spring为这个模板方法提供了几个实现,我们以AbstractRefreshableWebApplicationContext#postProcessBeanFactory为例看看这个实现

3.4.1 AbstractRefreshableWebApplicationContext#postProcessBeanFactory
@Override
	protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		/**
		 * 注册一个BeanPostProcessor---ServletContextAwareProcessor,用于bean初始化之前
		 * 执行回调方法检查bean是否实现ServletContextAware和ServletConfigAware接口,
		 * 如果是,那么注入依赖ServletContext和ServletConfig
		 */
		beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
		/**
		 * 上面已经处理了这2种依赖,下面直接忽略掉
		 */
		beanFactory.ignoreDependencyInterface(ServletContextAware.class);
		beanFactory.ignoreDependencyInterface(ServletConfigAware.class);

		/**
		 * 非常重要:注册web容器需要用到的相关作用域bean:
		 * 1、request
		 * 2、session
		 * 3、globalSession
		 * 4、application
		 */
		WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
		/**
		 * 注册环境相关bean
		 */
		WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
	}

这里面有个细节非常重要:
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext)
这行代码用于注册request、session、globalSession、application这4种web容器相关作用域bean。
有什么用呢?我们可以在Controller中以下面这种方式注入每次请求的request、response以及session

@RestController
public class ProjectController {

    @Autowired
    private HttpServletRequest request;
    @Autowired
    private HttpServletResponse response;
    @Autowired
    private HttpSession session;
}

此处注入的request、response、session将绑定每次请求的真实request、response、session,而且线程安全!!!
至于为什么可以这么用,这个涉及到Spring MVC中DispatcherServlet以及IOC容器依赖装配相关内容,又是一个很大的领域,将在本系列后续博客中阐述。

3.5 invokeBeanFactoryPostProcessors(beanFactory)

在1个bean的生命周期中需要经过多步操作才能最终使用,其中第一步就是执行BeanFactoryPostProcessor的回调方法
postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)。

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		/**
		 * 调用所有注册的BeanFactoryPostProcessor实现类的postProcessBeanFactory(factory) 回调方法
		 */
		PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

		/**
		 * 再次检查beanFactory是否注册了ltw,如果是那就注册BeanPostProcessor---LoadTimeWeaverAwareProcessor
		 */
		if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
			beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
			beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
		}
	}

3.6 registerBeanPostProcessors(beanFactory)

在前面的工作中,我们已经添加了所有的BeanPostProcessor(Spring自动添加,如果有必要,你也可以手动添加一些感兴趣的BeanPostProcessor),在这一步中,需要将这些BeanPostProcessor注册到beanFactory中,注意仅仅是注册,还没有调用里面的回调方法(回调方法会在后面bean初始化前后执行)

protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
	PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}

3.7 initMessageSource()

初始化当前容器的MessageSource,国际化相关,非重点,直接跳过

3.8 initApplicationEventMulticaster()

初始化当前容器的ApplicationEventMulticaster,事件广播器,跳过

3.9 onRefresh()

又是一个扩展点,模板方法,在context子类中初始化一些其他特殊的bean(在单例bean初始化之前执行),不是我们关注的重点,跳过

3.10 registerListeners()

注册实现了ApplicationListener接口的bean为监听器,用于监听容器事件,不展开说。

protected void registerListeners() {
		// Register statically specified listeners first.
		for (ApplicationListener<?> listener : getApplicationListeners()) {
			getApplicationEventMulticaster().addApplicationListener(listener);
		}

		// Do not initialize FactoryBeans here: We need to leave all regular beans
		// uninitialized to let post-processors apply to them!
		String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
		for (String listenerBeanName : listenerBeanNames) {
			getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
		}

		// Publish early application events now that we finally have a multicaster...
		Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
		this.earlyApplicationEvents = null;
		if (earlyEventsToProcess != null) {
			for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
				getApplicationEventMulticaster().multicastEvent(earlyEvent);
			}
		}
	}

3.11 finishBeanFactoryInitialization(beanFactory)

终于来到了最重要的部分,这一步是refresh方法最重要的一步,也是最难最复杂的部分,用于实例化所有非懒加载的单例bean。

在上面的ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()中,
只是注册了beanDefinition,还没有真正实例化,在这里会进行真正的实例化,经过这一步,bean才算是真正可以使用了。

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
   	/**
   	 * Spring 3.0添加的属性转换服务,作为JavaBeans PropertyEditors的替代方案
   	 * 用于某些bean属性进行转换是使用,比较重要的一个点
   	 */
   	if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
   			beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
   		beanFactory.setConversionService(
   		//调用getBean提前初始化
   		beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
   }

   	// Register a default embedded value resolver if no bean post-processor
   	// (such as a PropertyPlaceholderConfigurer bean) registered any before:
   	// at this point, primarily for resolution in annotation attribute values.
   	if (!beanFactory.hasEmbeddedValueResolver()) {
   		beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
   	}

   	// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
   	String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
   	for (String weaverAwareName : weaverAwareNames) {
   		getBean(weaverAwareName);
   	}

   	//停止使用临时类加载器
   	beanFactory.setTempClassLoader(null);

   	//因为下一步就要与初始化所有非懒加载单例bean了,在这里冻结配置信息,即将所有definition元数据缓存起来,以避免被修改或者有任何后处理操作
   	beanFactory.freezeConfiguration();

   	//实例化所有剩余的非懒加载单例bean
   	beanFactory.preInstantiateSingletons();
   }

接下来就是beanFactory.preInstantiateSingletons()了,这里不继续展开,后续将分多篇博客来介绍里面的内容。

3.12 finishRefresh()

到这里,容器已经初始化完毕,会发布ContextRefreshedEvent。

传统的容器启动后执行初始化方法有3种方式:
1、在bean标签中指定init-method,或者@Bean(initMethod=???)
2、实现InitializingBean接口
3、使用@PostConstruct
这里我们可以利用这个事件实现第4种方式
比如实现ApplicationListener接口,当监听到ContextRefreshedEvent事件时,方法onApplicationEvent(event)
将被调用,然后做一些初始化工作!

protected void finishRefresh() {
	// Clear context-level resource caches (such as ASM metadata from scanning).
	clearResourceCaches();

	// Initialize lifecycle processor for this context.
	initLifecycleProcessor();

	// Propagate refresh to lifecycle processor first.
	getLifecycleProcessor().onRefresh();

	// Publish the final event.
	publishEvent(new ContextRefreshedEvent(this));

	// Participate in LiveBeansView MBean, if active.
	LiveBeansView.registerApplicationContext(this);
}

3.13 destroyBeans() & cancelRefresh(ex)

如果容器刷新异常,会执行这2个方法。

1、destroyBeans():如果容器刷新异常,销毁所有已经创建的单例bean
2、cancelRefresh(ex):设置active=false,表示容器没有刷新过,允许下一次刷新

protected void destroyBeans() {
	getBeanFactory().destroySingletons();
}
protected void cancelRefresh(BeansException ex) {
	this.active.set(false);
}

3.14 resetCommonCaches()

不管最终容器有没有成功刷新,都需要清除缓存数据

至此Spring IOC容器已启动完毕,可以愉快地使用啦!

本博客系作者原创,欢迎转载,但请注明出处,谢谢!
原文链接:https://blog.csdn.net/weixin_41042119/article/details/90107989

你可能感兴趣的:(Spring)