老生常谈:聊聊Spring Boot中的那些生命周期和其中的可扩展点

Spring Boot 专栏收录该内容
0 篇文章 0 订阅

前言

    Spring框架的IoC特性对IoC容器中的对象进行了统一的管理,一个对象从创建到销毁所要经历的一系列步骤和过程都由IoC容器进行了定义和管理,这一系列步骤通常被称为Bean的生命周期。Bean的生命周期可以大致分为两个部分:创建和销毁。而创建过程又可大致分为:1)实例化(Instantiation,为对象分配内存);2)初始化(Initialization,设置对象属性)。

可扩展点
可扩展点
可扩展点
可扩展点
开始
实例化
初始化
销毁
结束
图1 bean生命周期

    为什么要这样对Bean的生命周期进行划分呢?一方面是因为这些步骤对Bean的状态进行了实质性的改变,经过这些步骤,Bean和之前大不相同了;另一方面,从应用的角度讲,也是因为Spring框架在这些步骤的前后都埋下了可扩展点,可供用户进行一些定制化的操作,这样划分有助于我们理解这些定制化操作的时机的含义。
    但是对于Spring Boot来讲,用户可用的可扩展点不仅仅只存在于Bean的生命周期当中。能够影响到最终产出的Bean的某些操作,也不仅仅只存在于Bean的生命周期当中,如BeanFactoryPostProcessor在Bean实例化之修改了BeanDefinition。这些与Bean生命周期无关的可扩展点,往往与应用(SpringApplication)和容器(ApplicationContext)在其启停过程中所要必须经历的一些步骤相关。在这里我们拓宽一下生命周期的概念,把应用和容器从创建到销毁所要经历的一系列必经的步骤叫做其生命周期。这样通过梳理SpringApplication和ApplicationContext的生命周期,我们就可以了解到与应用和容器启停相关的可扩展点,并进行一些定制化的操作。 


在这里插入图片描述

图2 应用、容器和非懒加载单例Bean的创建顺序关系

    本文基于SpringBoot 2.1.4.RELEASE对SpringApplication、ApplicationContext的启停过程和Bean的生命周期进行大致的梳理(Bean的生命周期梳理侧重于非懒加载单例Bean),目的是找出其中可供用户进行定制操作的扩展点,并梳理下容器启停过程和Bean生命周期的关联。

可扩展点的种类

    Spring框架中预留的扩展方式主要有两种:实现特定接口的特定方法,该方法会在特定时机运行;继承父类,并重写父类在特定时机运行的方法。对于第二种方式,理论上来讲所有public和protected方法都可被子类重写而达到定制的目的,但实际上Spring Boot已经对大部分的方法写好了默认实现,这些实现也正是定义SpringBoot启停过程的基石。所以只有小部分方法是专门预留给子类进行扩展的,这些方法会在后续的梳理中加以区分。
    扩展方式可细分为:
    1) B e a n 自 身 方 法 \color{#FF00FF}{Bean自身方法} Bean。也就是通过配置或注解标识的init-method和desctroy-method。
    2) 接 口 \color{#228B22}{接口} 。实现该接口的Bean会在其生命周期或容器启停的某个时机调用该接口方法,根据应用范围还可细分为容器级别接口和Bean级别接口。容器级别接口一般在容器的尺度上进行操作,如BeanPostProcessor可对容器中的所有Bean进行操作;而Bean级别接口只会对实现该接口的Bean进行操作,典型的包括Aware系列接口,以及InitializingBean。
    3) 需 在 s p r i n g . f a c t o r i e s 中 配 置 的 接 口 \color{#0000FF}{需在spring.factories中配置的接口} spring.factories。需要配置是因为这些接口执行的时机在BeanDefinition导入容器之前,还不能通过BeanDefinition来生成实例,所以需要特殊配置一下来提前生成实例。
    4) 专 用 于 重 写 的 模 板 方 法 \color{#20B2AA}{专用于重写的模板方法} 。用于对SpringApplication和ApplicationContext的子类实现扩展逻辑,没有默认实现。
    5) 建 议 子 类 重 写 的 父 类 方 法 \color{#808000}{建议子类重写的父类方法} 。该类方法一般在父类已有默认实现,但子类也可添加额外的定制逻辑。
    6) 通 常 不 进 行 重 写 的 父 类 方 法 \color{#B22222}{通常不进行重写的父类方法} 。该类方法理论上可以由子类重写,但该类方法的默认实现一般都包含了Spring Boot启动的必要逻辑,一般不建议重写。

Spring Boot启动过程

在这里插入图片描述

图3 Spring Boot启动过程中的可扩展点

    Spring Boot启动过程及其中的可扩展点由上图所示,图中各个单元之间的箭头表示调用或顺次调用的意思,三条泳道分别代表应用、容器和Bean的启动过程。

1.SpringApplication的启动过程

    1)SpringApplication完成实例化后,首先会调用 S p r i n g A p p l i c a t i o n R u n L i s t e n e r \color{#0000FF}{SpringApplicationRunListener} SpringApplicationRunListener的starting方法。实际上 S p r i n g A p p l i c a t i o n R u n L i s t e n e r \color{#0000FF}{SpringApplicationRunListener} SpringApplicationRunListener的各个方法的调用时机标志了SpringApplication启动过程的各个关键节点。


在这里插入图片描述

图4 SpringApplicationRunListener接口方法标志了SpringApplication启停的各个阶段

    通过调用 S p r i n g A p p l i c a t i o n R u n L i s t e n e r \color{#0000FF}{SpringApplicationRunListener} SpringApplicationRunListener的starting方法,会触发事件发布器EventPublishingRunListener发布ApplicationStartingEvent事件。监听该类型事件的 A p p l i c a t i o n L i s t e n e r \color{#0000FF}{ApplicationListener} ApplicationListener接口会调用其onApplicationEvent方法。
    2)创建并配置完Environment(profile和properties信息)后,调用 S p r i n g A p p l i c a t i o n R u n L i s t e n e r \color{#0000FF}{SpringApplicationRunListener} SpringApplicationRunListener的environmentPrepared方法,同样会触发事件发布器发布ApplicationEnvironmentPreparedEvent,调用 A p p l i c a t i o n L i s t e n e r \color{#0000FF}{ApplicationListener} ApplicationListener的onApplicationEvent方法。
    3)接下来就开始了容器的创建和配置。首先进行ApplicationContext的实例化,随后调用 S p r i n g A p p l i c a t i o n 的 p o s t P r o c e s s A p p l i c a t i o n C o n t e x t 方 法 \color{#808000}{SpringApplication的postProcessApplicationContext方法} SpringApplicationpostProcessApplicationContext,可对ApplicationContext进行部分初始化操作,该方法可由子类添加定制的容器初始化逻辑。
    4)调用 S p r i n g A p p l i c a t i o n 的 a p p l y I n i t i a l i z e r s 方 法 \color{#B22222}{SpringApplication的applyInitializers方法} SpringApplicationapplyInitializers,该方法的默认实现会调用 A p p l i c a t i o n C o n t e x t I n i t i a l i z e r \color{#0000FF}{ApplicationContextInitializer} ApplicationContextInitializer接口的initialize方法,用于对ApplicationContext进行初始化。
    5)完成ApplicationContext的初始化后,
调用 S p r i n g A p p l i c a t i o n R u n L i s t e n e r \color{#0000FF}{SpringApplicationRunListener} SpringApplicationRunListener的contextPrepared方法,发布ApplicationContextInitializedEvent并调用相应 A p p l i c a t i o n L i s t e n e r \color{#0000FF}{ApplicationListener} ApplicationListener的onApplicationEvent方法。
    6)调用 S p r i n g A p p l i c a t i o n 的 l o a d 方 法 \color{#B22222}{SpringApplication的load方法} SpringApplicationload,把SpringApplication初始化时导入的资源注册到ApplicationContext中统一管理。这样ApplicationContext就对其生前发生的事情有了足够的了解。
    7)调用 S p r i n g A p p l i c a t i o n R u n L i s t e n e r \color{#0000FF}{SpringApplicationRunListener} SpringApplicationRunListener的contextLoaded方法,发布ApplicationPreparedEvent并调用相应 A p p l i c a t i o n L i s t e n e r \color{#0000FF}{ApplicationListener} ApplicationListener的onApplicationEvent方法。
    8)调用 S p r i n g A p p l i c a t i o n 的 r e f r e s h 方 法 \color{#B22222}{SpringApplication的refresh方法} SpringApplicationrefresh,其中调用了 A b s t r a c t A p p l i c a t i o n C o n t e x t 的 r e f r e s h 方 法 \color{#B22222}{AbstractApplicationContext的refresh方法} AbstractApplicationContextrefresh,开始Bean的注入过程,并注册结束钩子用于容器销毁。
    9)调用 S p r i n g A p p l i c a t i o n 的 a f t e r R e f r e s h 方 法 \color{#20B2AA}{SpringApplication的afterRefresh方法} SpringApplicationafterRefresh。该方法由子类实现,用于在容器刷新完成后进行一些应用级别的操作。
    10)调用 S p r i n g A p p l i c a t i o n R u n L i s t e n e r \color{#0000FF}{SpringApplicationRunListener} SpringApplicationRunListener的started方法,发布ApplicationStartedEvent并调用相应 A p p l i c a t i o n L i s t e n e r \color{#228B22}{ApplicationListener} ApplicationListener的onApplicationEvent方法。此时容器已启动完成。
    11)调用 A p p l i c a t i o n R u n n e r \color{#228B22}{ApplicationRunner} ApplicationRunner C o m m a n d L i n e R u n n e r \color{#228B22}{CommandLineRunner} CommandLineRunner的run方法。用于在容器启动后执行定制操作。
    12)调用 S p r i n g A p p l i c a t i o n R u n L i s t e n e r \color{#0000FF}{SpringApplicationRunListener} SpringApplicationRunListener的running方法,发布ApplicationReadyEvent并调用相应 A p p l i c a t i o n L i s t e n e r \color{#228B22}{ApplicationListener} ApplicationListener的onApplicationEvent方法。完成应用的启动阶段。

/**
	 * Run the Spring application, creating and refreshing a new
	 * {@link ApplicationContext}.
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return a running {@link ApplicationContext}
	 */
	public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		//1)调用SpringApplicationRunListener的starting方法
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			//2)调用SpringApplicationRunListener的environmentPrepared方法
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			//3)实例化ApplicationContext
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			//3)~7)调用SpringApplication的prepareContext方法
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			//8)调用SpringApplication的refresh方法
			refreshContext(context);
			//9)调用SpringApplication的afterRefresh方法
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			//10)调用SpringApplicationRunListener的started方法
			listeners.started(context);
			//11)调用ApplicationRunner和CommandLineRunner的run方法
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			//12)调用SpringApplicationRunListener的running方法
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}
图5 SpringApplication启动代码主体
private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		context.setEnvironment(environment);
		//3)调用SpringApplication的postProcessApplicationContext
		postProcessApplicationContext(context);
		//4)调用SpringApplication的applyInitializers方法
		applyInitializers(context);
		//5)调用SpringApplicationRunListener的contextPrepared方法
		listeners.contextPrepared(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// Add boot specific singleton beans
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) {
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
		if (beanFactory instanceof DefaultListableBeanFactory) {
			((DefaultListableBeanFactory) beanFactory)
					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		// Load the sources
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		//6)调用SpringApplication的load方法
		load(context, sources.toArray(new Object[0]));
		//7)调用SpringApplicationRunListener的contextLoaded方法
		listeners.contextLoaded(context);
	}
图6 SpringApplication的prepareContext方法

2.ApplicationContext的启动过程

    ApplicationContext的启动过程已AbstractApplicationContext的refresh方法为主体,其中涉及到的步骤可大致概括为以下几类:
    1.ApplicationContext的实例化。
    2.ApplicationContext的初始化,包括其核心组件BeanFactory的初始化,以及其他组件(MessageSource、事件广播器)的初始化。
    3.非懒加载单例bean的创建。


在这里插入图片描述

图7 ApplicationContext的启动过程

    而在ApplicationContext和BeanFactory的创建过程中,框架提供了一些扩展点供用户进行定制操作,如修改BeanFactory管理的BeanDefinition信息,以及容器创建过程中的后处理操作等。具体步骤如下:
    1)在SpringApplication的启动过程的2)之后,进行ApplicationContext的实例化
    2)接下来会在SpringApplication的启动过程的3)和4)中进行部分初始化(后处理)操作。即 S p r i n g A p p l i c a t i o n 的 p o s t P r o c e s s A p p l i c a t i o n C o n t e x t 方 法 \color{#808000}{SpringApplication的postProcessApplicationContext方法} SpringApplicationpostProcessApplicationContext S p r i n g A p p l i c a t i o n 的 a p p l y I n i t i a l i z e r s 方 法 \color{#B22222}{SpringApplication的applyInitializers方法} SpringApplicationapplyInitializers。其中会调用 A p p l i c a t i o n C o n t e x t I n i t i a l i z e r 的 i n i t i a l i z e 方 法 \color{#B22222}{ApplicationContextInitializer的initialize方法} ApplicationContextInitializerinitialize
    3)在SpringApplication的启动过程的8),调用 S p r i n g A p p l i c a t i o n 的 r e f r e s h 方 法 \color{#B22222}{SpringApplication的refresh方法} SpringApplicationrefresh,即调用 A b s t r a c t A p p l i c a t i o n C o n t e x t 的 r e f r e s h 方 法 \color{#B22222}{AbstractApplicationContext的refresh方法} AbstractApplicationContextrefresh,进行ApplicationContext组件的初始化以及开始Bean的注入过程。
    (1)调用 A b s t r a c t A p p l i c a t i o n C o n t e x t 的 p r e p a r e R e f r e s h 方 法 \color{#B22222}{AbstractApplicationContext的prepareRefresh方法} AbstractApplicationContextprepareRefresh,其中会调用子类的 i n i t P r o p e r t y S o u r c e s 方 法 \color{#20B2AA}{initPropertySources方法} initPropertySources对配置资源进行一些定制的初始化处理。
    (2)调用 A b s t r a c t A p p l i c a t i o n C o n t e x t 的 o b t a i n F r e s h B e a n F a c t o r y 方 法 \color{#B22222}{AbstractApplicationContext的obtainFreshBeanFactory方法} AbstractApplicationContextobtainFreshBeanFactory,该方法调用了 r e f r e s h B e a n F a c t o r y 和 g e t B e a n F a c t o r y 方 法 \color{#20B2AA}{refreshBeanFactory和getBeanFactory方法} refreshBeanFactorygetBeanFactory,它们由子类实现,来获取不同ApplicationContext的不同的BeanFactory(实例化或初始化BeanFactory)。
    (3)调用 A b s t r a c t A p p l i c a t i o n C o n t e x t 的 p r e p a r e B e a n F a c t o r y 方 法 \color{#B22222}{AbstractApplicationContext的prepareBeanFactory方法} AbstractApplicationContextprepareBeanFactory,对beanFactory进行一些初始化操作。
    (4)调用 A b s t r a c t A p p l i c a t i o n C o n t e x t 的 p o s t P r o c e s s B e a n F a c t o r y 方 法 \color{#20B2AA}{AbstractApplicationContext的postProcessBeanFactory方法} AbstractApplicationContextpostProcessBeanFactory。该方法是预留扩展的beanFactory后处理方法。
    (5)调用 A b s t r a c t A p p l i c a t i o n C o n t e x t 的 i n v o k e B e a n F a c t o r y P o s t P r o c e s s o r s 方 法 \color{#B22222}{AbstractApplicationContext的invokeBeanFactoryPostProcessors方法} AbstractApplicationContextinvokeBeanFactoryPostProcessors。其中会调用 B e a n D e f i n i t i o n R e g i s t r y P o s t P r o c e s s o r \color{#228B22}{BeanDefinitionRegistryPostProcessor} BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法和 B e a n F a c t o r y P o s t P r o c e s s o r \color{#228B22}{BeanFactoryPostProcessor} BeanFactoryPostProcessor的postProcessBeanFactory方法。其中有后处理器从配置或注解中获取BeanDefinition并注册到容器上,并进行定制修改。自此就可以通过BeanDefinition来创建Bean了。
    (6)调用 A b s t r a c t A p p l i c a t i o n C o n t e x t 的 r e g i s t e r B e a n P o s t P r o c e s s o r s 方 法 \color{#B22222}{AbstractApplicationContext的registerBeanPostProcessors方法} AbstractApplicationContextregisterBeanPostProcessors。该方法把BeanPostProcessor注册到beanFactory,为后续步骤中的Bean创建添加后处理器。
    (7)调用 A b s t r a c t A p p l i c a t i o n C o n t e x t 的 i n i t M e s s a g e S o u r c e 方 法 \color{#B22222}{AbstractApplicationContext的initMessageSource方法} AbstractApplicationContextinitMessageSource。把实现MessageSource接口的类作为AbstractApplicationContext的成员变量。
    (8)调用 A b s t r a c t A p p l i c a t i o n C o n t e x t 的 i n i t A p p l i c a t i o n E v e n t M u l t i c a s t e r 方 法 \color{#B22222}{AbstractApplicationContext的initApplicationEventMulticaster方法} AbstractApplicationContextinitApplicationEventMulticaster。设置AbstractApplicationContext的事件广播器。后续的事件广播就由SpringApplication移交给ApplicationContext来做了。
    (9)调用 A b s t r a c t A p p l i c a t i o n C o n t e x t 的 o n R e f r e s h 方 法 \color{#20B2AA}{AbstractApplicationContext的onRefresh方法} AbstractApplicationContextonRefresh。该方法由可由子类实现,在实例化非懒加载单例bean之前来初始化一些特殊的、需要提前初始化的bean。
    (10)调用 A b s t r a c t A p p l i c a t i o n C o n t e x t 的 r e g i s t e r L i s t e n e r s 方 法 \color{#B22222}{AbstractApplicationContext的registerListeners方法} AbstractApplicationContextregisterListeners。把SpringApplication的listener交给ApplicationContext管理。
    (11)调用 A b s t r a c t A p p l i c a t i o n C o n t e x t 的 f i n i s h B e a n F a c t o r y I n i t i a l i z a t i o n 方 法 \color{#B22222}{AbstractApplicationContext的finishBeanFactoryInitialization方法} AbstractApplicationContextfinishBeanFactoryInitialization初始化剩余的未初始化的所有非懒加载单例bean。然后会调用 S m a r t I n i t i a l i z i n g S i n g l e t o n \color{#228B22}{SmartInitializingSingleton} SmartInitializingSingleton的afterSingletonsInstantiated方法。该方法专用于非懒加载单例bean在其创建完成后所要进行的定制操作。
    (12)调用 A b s t r a c t A p p l i c a t i o n C o n t e x t 的 f i n i s h R e f r e s h 方 法 \color{#B22222}{AbstractApplicationContext的finishRefresh方法} AbstractApplicationContextfinishRefresh。其中会调用 L i f e c y c l e P r o c e s s o r \color{#228B22}{LifecycleProcessor} LifecycleProcessor的onRefresh方法,该方法的默认实现会调用 S m a r t L i f e c y c l e \color{#228B22}{SmartLifecycle} SmartLifecycle的start方法。随后发布ContextRefreshedEvent并调用相应 A p p l i c a t i o n L i s t e n e r \color{#228B22}{ApplicationListener} ApplicationListener的onApplicationEvent方法。

	@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			//(1)Prepare this context for refreshing.
			prepareRefresh();

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

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

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

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

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

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

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

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

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

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

				// (12)Last step: publish corresponding event.
				finishRefresh();
			}
			catch (BeansException ex) {
				//exception handle...
			}
			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}
图8 ApplicationContext的启动主体:refresh方法

3.一般的非懒加载单例Bean在Spring Boot启动过程中的生命周期

    由前言所述,Bean的生命周期可以大致分为两个部分:创建和销毁。而创建过程又可大致分为:1)实例化;2)初始化。但如果把Bean的创建过程放在应用启动的背景来看,在其实例化之前还有BeanDefinition的创建过程,所以综合来看,Bean的创建过程可扩展为以下步骤:

可扩展点
可扩展点
可扩展点
可扩展点
开始
生成BeanDefinition
Bean实例化
Bean属性注入
结束
图9 Bean创建流程

    1)在ApplicationContext启动过程中的3)(5),即 A b s t r a c t A p p l i c a t i o n C o n t e x t 的 i n v o k e B e a n F a c t o r y P o s t P r o c e s s o r s 方 法 \color{#B22222}{AbstractApplicationContext的invokeBeanFactoryPostProcessors方法} AbstractApplicationContextinvokeBeanFactoryPostProcessors中,会调用 B e a n D e f i n i t i o n R e g i s t r y P o s t P r o c e s s o r \color{#228B22}{BeanDefinitionRegistryPostProcessor} BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法。其中有个重要的BeanDefinitionRegistryPostProcessor:根据注解或xml配置生成BeanDefinition并注册到BeanDefinitionRegistry。
    2)在ApplicationContext启动过程中的3)(5)中,调用 B e a n F a c t o r y P o s t P r o c e s s o r \color{#228B22}{BeanFactoryPostProcessor} BeanFactoryPostProcessor的postProcessBeanFactory方法,对BeanDefinition做定制化修改。
    3)接下来的步骤3)到步骤12)都是在ApplicationContext启动过程中的3)(11)即 A b s t r a c t A p p l i c a t i o n C o n t e x t 的 f i n i s h B e a n F a c t o r y I n i t i a l i z a t i o n 方 法 \color{#B22222}{AbstractApplicationContext的finishBeanFactoryInitialization方法} AbstractApplicationContextfinishBeanFactoryInitialization中进行bean的实例化和初始化操作。在进行实例化之前,首先调用 I n s t a n t i a t i o n A w a r e B e a n P o s t P r o c e s s o r \color{#228B22}{InstantiationAwareBeanPostProcessor} InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation方法。该方法可跳过后续实例化步骤,并包办实例化初始化过程,可用于创建代理类。
    4) 调 用 S m a r t I n s t a n t i a t i o n A w a r e B e a n P o s t P r o c e s s o r 的 d e t e r m i n e C a n d i d a t e C o n s t r u c t o r s \color{#228B22}{调用SmartInstantiationAwareBeanPostProcessor的determineCandidateConstructors} SmartInstantiationAwareBeanPostProcessordetermineCandidateConstructors方法,可用来选择用于实例化的构造方法。
    5)实例化bean,然后调用 M e r g e d B e a n D e f i n i t i o n P o s t P r o c e s s o r \color{#228B22}{MergedBeanDefinitionPostProcessor} MergedBeanDefinitionPostProcessor的postProcessMergedBeanDefinition方法。该方法提供了在bean的生命周期中修改BeanDefinition的途径,而步骤2)提供了在ApplicationContext启动时修改BeanDefinition的途径。
    6)调用 I n s t a n t i a t i o n A w a r e B e a n P o s t P r o c e s s o r \color{#228B22}{InstantiationAwareBeanPostProcessor} InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation方法。此时bean已经实例化,但属性还未设置。这里可以实现定制的属性注入逻辑,并跳过默认实现的属性注入步骤。
    7)从BeanDefinition获取要注入的属性值。调用 I n s t a n t i a t i o n A w a r e B e a n P o s t P r o c e s s o r \color{#228B22}{InstantiationAwareBeanPostProcessor} InstantiationAwareBeanPostProcessor的postProcessProperties和postProcessPropertyValues方法对其进行定制处理。
    8)注入属性值,调用 B e a n N a m e A w a r e \color{#228B22}{BeanNameAware} BeanNameAware的setBeanName方法、 B e a n C l a s s L o a d e r A w a r e \color{#228B22}{BeanClassLoaderAware} BeanClassLoaderAware的setBeanClassLoader方法、 B e a n F a c t o r y A w a r e \color{#228B22}{BeanFactoryAware} BeanFactoryAware的setBeanFactory方法,使bean获取BeanName、BeanClassLoader、BeanFactory进行相应操作。
    9)调用 B e a n P o s t P r o c e s s o r \color{#228B22}{BeanPostProcessor} BeanPostProcessor的postProcessBeforeInitialization方法。此时属性值已注入,init方法尚未调用。其中有个ApplicationContextAwareProcessor调用了各种Aware接口方法。
    10)调用 I n i t i a l i z i n g B e a n \color{#228B22}{InitializingBean} InitializingBean的afterPropertiesSet方法。该方法一般用于对bean实例的配置进行校验,或在属性值注入后进行最终的初始化操作。
    11)调用BeanDefinition中设置的 i n i t M e t h o d \color{#FF00FF}{initMethod} initMethod(可以由@Bean的initMethod属性设置,也可由xml配置文件的init-method设置。
    12)调用 B e a n P o s t P r o c e s s o r \color{#228B22}{BeanPostProcessor} BeanPostProcessor的postProcessAfterInitialization方法。此时Bean的初始化过程已经完成。
    13)在ApplicationContext启动过程中的3)(12),会调用 L i f e c y c l e P r o c e s s o r \color{#228B22}{LifecycleProcessor} LifecycleProcessor的onRefresh方法,该方法的默认实现会调用 S m a r t L i f e c y c l e \color{#228B22}{SmartLifecycle} SmartLifecycle的start方法。该方法一般可用于开启一些异步过程。
    注意:bean生命周期的步骤5),可以选用用Supplier、FactoryMethod和autowireConstructor进行实例化,bean生命周期与上述步骤略有不同。此外,若bean实现了FactoryBean,生命周期中某些步骤也略有不同(待后续总结)。

Spring Boot结束过程

在这里插入图片描述

图10 Spring Boot异常处理和结束过程中的可扩展点

1.SpringApplication启动时的异常处理过程

    当SpringApplication启动时出现异常后,需要处理如下几件事:错误码的生成、发布应用内事件、向用户报告异常和关闭容器。具体步骤如下:
    1)在SpringApplication的启动过程中出现异常时,首先通过异常类型来获取错误码,并发布ExitCodeEvent并调用相应 A p p l i c a t i o n L i s t e n e r \color{#228B22}{ApplicationListener} ApplicationListener的onApplicationEvent方法。
    2)调用 S p r i n g A p p l i c a t i o n R u n L i s t e n e r \color{#0000FF}{SpringApplicationRunListener} SpringApplicationRunListener的failed方法,发布ApplicationFailedEvent并调用相应 A p p l i c a t i o n L i s t e n e r \color{#228B22}{ApplicationListener} ApplicationListener的onApplicationEvent方法。
    3)调用 S p r i n g B o o t E x c e p t i o n R e p o r t e r \color{#228B22}{SpringBootExceptionReporter} SpringBootExceptionReporter的reportException方法。该方法可定制化实现,用于报告启动错误信息。
    4)接下来会调用 A b s t r a c t A p p l i c a t i o n C o n t e x t 的 d o C l o s e 方 法 \color{#B22222}{AbstractApplicationContext的doClose方法} AbstractApplicationContextdoClose,即容器关闭的过程。有关SpringApplication启动时的异常处理过程到此结束。

private void handleRunFailure(ConfigurableApplicationContext context,
  		Throwable exception,
  		Collection<SpringBootExceptionReporter> exceptionReporters,
  		SpringApplicationRunListeners listeners) {
  	try {
  		try {
  			//1)错误码处理
  			handleExitCode(context, exception);
  			if (listeners != null) {
  				//2)发布事件
  				listeners.failed(context, exception);
  			}
  		}
  		finally {
  			//3)定制错误报告
  			reportFailure(exceptionReporters, exception);
  			if (context != null) {
  				//4)容器资源释放
  				context.close();
  			}
  		}
  	} catch (Exception ex) {
  		logger.warn("Unable to close ApplicationContext", ex);
  	}
  	ReflectionUtils.rethrowRuntimeException(exception);
}
图11 SpringApplication启动时的异常处理过程:handleRunFailure方法

2.ApplicationContext的关闭过程

    容器的关闭过程大致包括:发布事件、执行容器内bean生命周期的销毁部分、关闭容器。具体步骤如下:
    1)当SpringApplication启动时出现异常,或程序结束调用关闭钩子时,会调用 A b s t r a c t A p p l i c a t i o n C o n t e x t 的 d o C l o s e 方 法 \color{#B22222}{AbstractApplicationContext的doClose方法} AbstractApplicationContextdoClose,开始关闭容器的过程。
    2)首先会发布ContextClosedEvent并调用相应 A p p l i c a t i o n L i s t e n e r \color{#228B22}{ApplicationListener} ApplicationListener的onApplicationEvent方法。并由 L i f e c y c l e \color{#228B22}{Lifecycle} Lifecycle的isRunning方法的返回值决定是否去调用 S m a r t L i f e c y c l e \color{#228B22}{SmartLifecycle} SmartLifecycle L i f e c y c l e \color{#228B22}{Lifecycle} Lifecycle的stop方法。
    3)调用 A b s t r a c t A p p l i c a t i o n C o n t e x t 的 d e s t r o y B e a n s \color{#808000}{AbstractApplicationContext的destroyBeans} AbstractApplicationContextdestroyBeans方法。该方法用beanFactory来销毁所有容器管理的bean,可由子类添加定制逻辑。
    4)处理完beanFactory管理的bean后,调用 A b s t r a c t A p p l i c a t i o n C o n t e x t 的 c l o s e B e a n F a c t o r y 方 法 \color{#20B2AA}{AbstractApplicationContext的closeBeanFactory方法} AbstractApplicationContextcloseBeanFactory,对ApplicationContext定制实现的BeanFactory做定制的关闭处理。
    5)调用 A b s t r a c t A p p l i c a t i o n C o n t e x t 的 o n C l o s e 方 法 \color{#20B2AA}{AbstractApplicationContext的onClose方法} AbstractApplicationContextonClose,该方法用于给子类实现定制的额外的关闭操作。

/**
   * Actually performs context closing: publishes a ContextClosedEvent and
   * destroys the singletons in the bean factory of this application context.
   * <p>Called by both {@code close()} and a JVM shutdown hook, if any.
   * @see org.springframework.context.event.ContextClosedEvent
   * @see #destroyBeans()
   * @see #close()
   * @see #registerShutdownHook()
   */
  protected void doClose() {
  	// Check whether an actual close attempt is necessary...
  	if (this.active.get() && this.closed.compareAndSet(false, true)) {
  		if (logger.isDebugEnabled()) {
  			logger.debug("Closing " + this);
  		}

  		LiveBeansView.unregisterApplicationContext(this);

  		try {
  			// 2) Publish shutdown event.
  			publishEvent(new ContextClosedEvent(this));
  		}
  		catch (Throwable ex) {
  			logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
  		}

  		// 2) Stop all Lifecycle beans, to avoid delays during individual destruction.
  		if (this.lifecycleProcessor != null) {
  			try {
  				this.lifecycleProcessor.onClose();
  			}
  			catch (Throwable ex) {
  				logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
  			}
  		}

  		// 3) Destroy all cached singletons in the context's BeanFactory.
  		destroyBeans();

  		// 4) Close the state of this context itself.
  		closeBeanFactory();

  		// 5) Let subclasses do some final clean-up if they wish...
  		onClose();

  		// Reset local application listeners to pre-refresh state.
  		if (this.earlyApplicationListeners != null) {
  			this.applicationListeners.clear();
  			this.applicationListeners.addAll(this.earlyApplicationListeners);
  		}
  		// Switch to inactive.
  		this.active.set(false);
  	}
  }
图12 ApplicationContext的关闭过程:doClose方法

3.单例Bean销毁过程

    单例Bean的销毁过程是由 A b s t r a c t A p p l i c a t i o n C o n t e x t 的 d e s t r o y B e a n s \color{#808000}{AbstractApplicationContext的destroyBeans} AbstractApplicationContextdestroyBeans方法实现的,其中调用了ConfigurableBeanFactory的destroySingletons方法,具体由实现了ConfigurableBeanFactory接口的类来实现。Spring Boot的默认实现调用了DefaultSingletonBeanRegistry的destroySingletons进行销毁操作,其中会对实现了 D i s p o s a b l e B e a n \color{#228B22}{DisposableBean} DisposableBean接口的bean进行定制操作。在默认实现中,每个 D i s p o s a b l e B e a n \color{#228B22}{DisposableBean} DisposableBean都由DisposableBeanAdapter包装了一层, D i s p o s a b l e B e a n \color{#228B22}{DisposableBean} DisposableBean的销毁步骤实际上由DisposableBeanAdapter的destroy方法进行了制定,其具体步骤如下:
    1)从容器的Bean列表中把Bean剔除后,调用 D e s t r u c t i o n A w a r e B e a n P o s t P r o c e s s o r \color{#228B22}{DestructionAwareBeanPostProcessor} DestructionAwareBeanPostProcessor的postProcessBeforeDestruction方法,用于在Bean销毁前对Bean进行处理。
    2)调用 D i s p o s a b l e B e a n \color{#228B22}{DisposableBean} DisposableBean的destroy方法。
    3)调用BeanDefinition中设置的 d e s t r o y M e t h o d \color{#FF00FF}{destroyMethod} destroyMethod

  @Override
  public void destroy() {
  	//1)调用DestructionAwareBeanPostProcessor的postProcessBeforeDestruction方法
  	if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
  		for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
  			processor.postProcessBeforeDestruction(this.bean, this.beanName);
  		}
  	}
  	//2)调用DisposableBean的destroy方法。
  	if (this.invokeDisposableBean) {
  		if (logger.isTraceEnabled()) {
  			logger.trace("Invoking destroy() on bean with name '" + this.beanName + "'");
  		}
  		try {	
  			if (System.getSecurityManager() != null) {
  				AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
  					((DisposableBean) this.bean).destroy();
  					return null;
  				}, this.acc);
  			}
  			else {
  				((DisposableBean) this.bean).destroy();
  			}
  		}
  		catch (Throwable ex) {
  			//异常日志处理
  		}
  	}
  	//3)调用BeanDefinition中设置的destroyMethod
  	if (this.destroyMethod != null) {
  		invokeCustomDestroyMethod(this.destroyMethod);
  	}
  	else if (this.destroyMethodName != null) {
  		Method methodToCall = determineDestroyMethod(this.destroyMethodName);
  		if (methodToCall != null) {
  			invokeCustomDestroyMethod(methodToCall);
  		}
  	}
  }
图13 DisposableBeanAdapter的destroy方法

总结

    到这里,我们对SpringBoot启停过程中有关应用、容器及Bean的流程和可扩展点进行了一个大致的梳理。在这些可扩展点中,最常用的就是通过实现接口来进行扩展了,如实现 I n i t i a l i z i n g B e a n \color{#228B22}{InitializingBean} InitializingBean接口进行bean的定制初始化操作,或实现 B e a n P o s t P r o c e s s o r \color{#228B22}{BeanPostProcessor} BeanPostProcessor对所有bean进行相关操作等等。而需在spring.factories中配置的接口和需要在通过重写SpringApplication及AbstractApplicationContext的方法则侧重于在容器启动过程中进行定制操作,这些方式更多地被Spring Boot框架本身采用,来实现框架各种丰富的功能。所以在应用启停过程中,除了核心的组件ApplicationContext和beanFactory,其他应用和容器所用的各种组件及后处理器,也为应用的正常运转和实现各种功能起到了重要的作用。本文从应用及开发的角度梳理了Spring Boot框架的启停步骤,但其实要想真正的学以致用,就应该了解一下框架本身是如何运用这些可扩展点来丰富框架功能的。下一篇文章中,我们就来聊聊Spring Boot框架中那些已经实现了的可扩展点的应用。

参考文献

Spring Bean的生命周期分析 https://blog.csdn.net/xpsallwell/article/details/87938536
spring4.1.8扩展实战之七:控制bean(BeanPostProcessor接口) https://blog.csdn.net/boling_cavalry/article/details/82250986
spring4.1.8扩展实战之六:注册bean到spring容器(BeanDefinitionRegistryPostProcessor接口) https://blog.csdn.net/boling_cavalry/article/details/82193692
spring4.1.8扩展实战之五:改变bean的定义(BeanFactoryPostProcessor接口) https://blog.csdn.net/boling_cavalry/article/details/82083889
spring4.1.8扩展实战之四:感知spring容器变化(SmartLifecycle接口) https://blog.csdn.net/boling_cavalry/article/details/82051356
spring4.1.8扩展实战之三:广播与监听 https://blog.csdn.net/boling_cavalry/article/details/81697314
spring4.1.8扩展实战之二:Aware接口揭秘 https://blog.csdn.net/boling_cavalry/article/details/81611426
SpringBoot应用使用自定义的ApplicationContext实现类 https://blog.csdn.net/boling_cavalry/article/details/81587556
【小家Spring】Spring解析@Configuration注解的处理器:ConfigurationClassPostProcessor(ConfigurationClassParser) https://blog.csdn.net/f641385712/article/details/88095165

展开阅读全文
  • 2
    点赞
  • 0
    评论
  • 8
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值