前言
可扩展点的种类
Spring Boot启动过程
  1.SpringApplication的启动过程
  2.ApplicationContext的启动过程
  3.一般的非懒加载单例Bean在Spring Boot启动过程中的生命周期
Spring Boot结束过程
  1.SpringApplication启动时的异常处理过程
  2.ApplicationContext的关闭过程
  3.单例Bean销毁过程
总结
 
前言
    Spring框架的IoC特性对IoC容器中的对象进行了统一的管理,一个对象从创建到销毁所要经历的一系列步骤和过程都由IoC容器进行了定义和管理,这一系列步骤通常被称为Bean的生命周期。Bean的生命周期可以大致分为两个部分:创建和销毁。而创建过程又可大致分为:1)实例化(Instantiation,为对象分配内存);2)初始化(Initialization,设置对象属性)。



    为什么要这样对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)Bean自身方法\color{#FF00FF}{Bean自身方法}Bean自身方法。也就是通过配置或注解标识的init-method和desctroy-method。
    2)接口\color{#228B22}{接口}接口。实现该接口的Bean会在其生命周期或容器启停的某个时机调用该接口方法,根据应用范围还可细分为容器级别接口和Bean级别接口。容器级别接口一般在容器的尺度上进行操作,如BeanPostProcessor可对容器中的所有Bean进行操作;而Bean级别接口只会对实现该接口的Bean进行操作,典型的包括Aware系列接口,以及InitializingBean。
    3)需在spring.factories中配置的接口\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完成实例化后,首先会调用SpringApplicationRunListener\color{#0000FF}{SpringApplicationRunListener}SpringApplicationRunListener的starting方法。实际上SpringApplicationRunListener\color{#0000FF}{SpringApplicationRunListener}SpringApplicationRunListener的各个方法的调用时机标志了SpringApplication启动过程的各个关键节点。

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

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

  SpringApplication启动代码主体

  /**
* 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;
}

SpringApplication的prepareContext方法

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);
}


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)中进行部分初始化(后处理)操作。即SpringApplication的postProcessApplicationContext方法\color{#808000}{SpringApplication的postProcessApplicationContext方法}SpringApplication的postProcessApplicationContext方法和SpringApplication的applyInitializers方法\color{#B22222}{SpringApplication的applyInitializers方法}SpringApplication的applyInitializers方法。其中会调用ApplicationContextInitializer的initialize方法\color{#B22222}{ApplicationContextInitializer的initialize方法}ApplicationContextInitializer的initialize方法。
    3)在SpringApplication的启动过程的8),调用SpringApplication的refresh方法\color{#B22222}{SpringApplication的refresh方法}SpringApplication的refresh方法,即调用AbstractApplicationContext的refresh方法\color{#B22222}{AbstractApplicationContext的refresh方法}AbstractApplicationContext的refresh方法,进行ApplicationContext组件的初始化以及开始Bean的注入过程。
    (1)调用AbstractApplicationContext的prepareRefresh方法\color{#B22222}{AbstractApplicationContext的prepareRefresh方法}AbstractApplicationContext的prepareRefresh方法,其中会调用子类的initPropertySources方法\color{#20B2AA}{initPropertySources方法}initPropertySources方法对配置资源进行一些定制的初始化处理。
    (2)调用AbstractApplicationContext的obtainFreshBeanFactory方法\color{#B22222}{AbstractApplicationContext的obtainFreshBeanFactory方法}AbstractApplicationContext的obtainFreshBeanFactory方法,该方法调用了refreshBeanFactory和getBeanFactory方法\color{#20B2AA}{refreshBeanFactory和getBeanFactory方法}refreshBeanFactory和getBeanFactory方法,它们由子类实现,来获取不同ApplicationContext的不同的BeanFactory(实例化或初始化BeanFactory)。
    (3)调用AbstractApplicationContext的prepareBeanFactory方法\color{#B22222}{AbstractApplicationContext的prepareBeanFactory方法}AbstractApplicationContext的prepareBeanFactory方法,对beanFactory进行一些初始化操作。
    (4)调用AbstractApplicationContext的postProcessBeanFactory方法\color{#20B2AA}{AbstractApplicationContext的postProcessBeanFactory方法}AbstractApplicationContext的postProcessBeanFactory方法。该方法是预留扩展的beanFactory后处理方法。
    (5)调用AbstractApplicationContext的invokeBeanFactoryPostProcessors方法\color{#B22222}{AbstractApplicationContext的invokeBeanFactoryPostProcessors方法}AbstractApplicationContext的invokeBeanFactoryPostProcessors方法。其中会调用BeanDefinitionRegistryPostProcessor\color{#228B22}{BeanDefinitionRegistryPostProcessor}BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法和BeanFactoryPostProcessor\color{#228B22}{BeanFactoryPostProcessor}BeanFactoryPostProcessor的postProcessBeanFactory方法。其中有后处理器从配置或注解中获取BeanDefinition并注册到容器上,并进行定制修改。自此就可以通过BeanDefinition来创建Bean了。
    (6)调用AbstractApplicationContext的registerBeanPostProcessors方法\color{#B22222}{AbstractApplicationContext的registerBeanPostProcessors方法}AbstractApplicationContext的registerBeanPostProcessors方法。该方法把BeanPostProcessor注册到beanFactory,为后续步骤中的Bean创建添加后处理器。
    (7)调用AbstractApplicationContext的initMessageSource方法\color{#B22222}{AbstractApplicationContext的initMessageSource方法}AbstractApplicationContext的initMessageSource方法。把实现MessageSource接口的类作为AbstractApplicationContext的成员变量。
    (8)调用AbstractApplicationContext的initApplicationEventMulticaster方法\color{#B22222}{AbstractApplicationContext的initApplicationEventMulticaster方法}AbstractApplicationContext的initApplicationEventMulticaster方法。设置AbstractApplicationContext的事件广播器。后续的事件广播就由SpringApplication移交给ApplicationContext来做了。
    (9)调用AbstractApplicationContext的onRefresh方法\color{#20B2AA}{AbstractApplicationContext的onRefresh方法}AbstractApplicationContext的onRefresh方法。该方法由可由子类实现,在实例化非懒加载单例bean之前来初始化一些特殊的、需要提前初始化的bean。
    (10)调用AbstractApplicationContext的registerListeners方法\color{#B22222}{AbstractApplicationContext的registerListeners方法}AbstractApplicationContext的registerListeners方法。把SpringApplication的listener交给ApplicationContext管理。
    (11)调用AbstractApplicationContext的finishBeanFactoryInitialization方法\color{#B22222}{AbstractApplicationContext的finishBeanFactoryInitialization方法}AbstractApplicationContext的finishBeanFactoryInitialization方法。初始化剩余的未初始化的所有非懒加载单例bean。然后会调用SmartInitializingSingleton\color{#228B22}{SmartInitializingSingleton}SmartInitializingSingleton的afterSingletonsInstantiated方法。该方法专用于非懒加载单例bean在其创建完成后所要进行的定制操作。
    (12)调用AbstractApplicationContext的finishRefresh方法\color{#B22222}{AbstractApplicationContext的finishRefresh方法}AbstractApplicationContext的finishRefresh方法。其中会调用LifecycleProcessor\color{#228B22}{LifecycleProcessor}LifecycleProcessor的onRefresh方法,该方法的默认实现会调用SmartLifecycle\color{#228B22}{SmartLifecycle}SmartLifecycle的start方法。随后发布ContextRefreshedEvent并调用相应ApplicationListener\color{#228B22}{ApplicationListener}ApplicationListener的onApplicationEvent方法。

ApplicationContext的启动主体:refresh方法

@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();
}
}
}


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


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

Spring Boot结束过程


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

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

SpringApplication启动时的异常处理过程:handleRunFailure方法

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);
}


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

ApplicationContext的关闭过程:doClose方法

/**
* 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);
}
}


3.单例Bean销毁过程
    单例Bean的销毁过程是由AbstractApplicationContext的destroyBeans\color{#808000}{AbstractApplicationContext的destroyBeans}AbstractApplicationContext的destroyBeans方法实现的,其中调用了ConfigurableBeanFactory的destroySingletons方法,具体由实现了ConfigurableBeanFactory接口的类来实现。Spring Boot的默认实现调用了DefaultSingletonBeanRegistry的destroySingletons进行销毁操作,其中会对实现了DisposableBean\color{#228B22}{DisposableBean}DisposableBean接口的bean进行定制操作。在默认实现中,每个DisposableBean\color{#228B22}{DisposableBean}DisposableBean都由DisposableBeanAdapter包装了一层,DisposableBean\color{#228B22}{DisposableBean}DisposableBean的销毁步骤实际上由DisposableBeanAdapter的destroy方法进行了制定,其具体步骤如下:
    1)从容器的Bean列表中把Bean剔除后,调用DestructionAwareBeanPostProcessor\color{#228B22}{DestructionAwareBeanPostProcessor}DestructionAwareBeanPostProcessor的postProcessBeforeDestruction方法,用于在Bean销毁前对Bean进行处理。
    2)调用DisposableBean\color{#228B22}{DisposableBean}DisposableBean的destroy方法。
    3)调用BeanDefinition中设置的destroyMethod\color{#FF00FF}{destroyMethod}destroyMethod。

 DisposableBeanAdapter的destroy方法

@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);
}
}
}


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

原文链接:https://blog.csdn.net/dlxi12345/article/details/93518342

Spring Boot中的那些生命周期和其中的可扩展点(转)的更多相关文章

  1. Spring 容器中 Bean 的生命周期

    Spring 容器中 Bean 的生命周期 1. init-method 和 destory-method 方法 Spring 初始化 bean 或销毁 bean 时,有时需要作一些处理工作,因此 s ...

  2. (spring-第1回【IoC基础篇】)Spring容器中Bean的生命周期

    日出日落,春去秋来,花随流水,北雁南飞,世间万物皆有生死轮回.从调用XML中的Bean配置信息,到应用到具体实例中,再到销毁,Bean也有属于它的生命周期. 人类大脑对图像的认知能力永远高于文字,因此 ...

  3. IoC基础篇(一)--- Spring容器中Bean的生命周期

    日出日落,春去秋来,花随流水,北雁南飞,世间万物皆有生死轮回.从调用XML中的Bean配置信息,到应用到具体实例中,再到销毁,Bean也有属于它的生命周期. 人类大脑对图像的认知能力永远高于文字,因此 ...

  4. Spring容器中bean的生命周期以及关注spring bean对象的后置处理器:BeanPostProcessor(一个接口)

    Spring IOC 容器对 Bean 的生命周期进行管理的过程: 1.通过构造器或工厂方法创建 Bean 实例 2.为 Bean 的属性设置值和对其他 Bean 的引用 3.将 Bean 实例传递给 ...

  5. spring框架中Bean的生命周期

    一.Bean 的完整生命周期 在传统的Java应用中,bean的生命周期很简单,使用Java关键字 new 进行Bean 的实例化,然后该Bean 就能够使用了.一旦bean不再被使用,则由Java自 ...

  6. spring ApplicationContext中Bean的生命周期

    AbstractApplicationContext Spring的AbstractApplicationContext是ApplicationContext的抽象实现类,该抽象类的refresh方法 ...

  7. 解释Spring框架中bean的生命周期?

    Spring容器 从XML 文件中读取bean的定义,并实例化bean. Spring根据bean的定义填充所有的属性. 如果bean实现了BeanNameAware 接口,Spring 传递bean ...

  8. 解释 Spring 框架中 bean 的生命周期?

    Spring 容器 从 XML 文件中读取 bean 的定义,并实例化 bean. Spring 根据 bean 的定义填充所有的属性. 如果 bean 实现了 BeanNameAware 接口,Sp ...

  9. 解释 Spring 框架中 bean 的生命周期?

    Spring 容器 从 XML 文件中读取 bean 的定义,并实例化 bean. Spring 根据 bean 的定义填充所有的属性. 如果 bean 实现了 BeanNameAware 接口,Sp ...

随机推荐

  1. window系统安装mysql

    下载地址(其他图形界面) https://dev.mysql.com/downloads/windows/installer/如果下载win7 64位,在下载后出现不兼容,可以在window窗口菜单中 ...

  2. 七、.net core(.NET 6)使用Serilog进行配置和实现日志记录

    使用Serilog来实现日志记录 先安装Serilog六件套神装包: 也可以对个别相应的包进行删除等,都是可以的.例如,标注的1是读取配置文件的,如果不需要通过配置文件进行操作,就可以使用这个包.2是 ...

  3. Could not get JDBC Connection排查

    最近在维护的一个比较旧的项目,发现总是隔一段时间JDBC就报错: Could not get JDBC Connection; nested exception is org.apache.commo ...

  4. Caffe实现概述

    Caffe实现概述 目录 一.caffe配置文件介绍 二.标准层的定义 三.网络微调技巧 四.Linux脚本使用及LMDB文件生成 五.带你设计一个Caffe网络,用于分类任务 一.caffe配置文件 ...

  5. 使用CUDA Warp-Level级原语

    使用CUDA Warp-Level级原语 NVIDIA GPU以SIMT(单指令,多线程)的方式执行称为warps 的线程组.许多CUDA程序通过利用warp执行来实现高性能.本文将展示如何使用cud ...

  6. IDEA骚技巧

    1. var 声明 2. null 判空 3. notnull 判非空 4. nn 判非空 5. for 遍历 6. fori 带索引的遍历 7. not 取反 8. if 条件判断 9. cast ...

  7. Floyd最短路及路径输出

    引例 下图表示城市之间的交通路网,线段上的数字表示费用.如图,求$V_{1}$→$V_{n}$最短路径长度及路径 样例数据 输入 10 0 2 5 1 0 0 0 0 0 0 0 0 0 0 12 1 ...

  8. 文氏电桥振荡电路原理详解及Multisim实例仿真

    文氏电桥振荡电路(Wien bridge oscillator circuit),简称"文氏电桥",是一种适于产生正弦波信号的振荡电路之一,此电路振荡稳定且输出波形良好,在较宽的频 ...

  9. Pandas高级教程之:category数据类型

    目录 简介 创建category 使用Series创建 使用DF创建 创建控制 转换为原始类型 categories的操作 获取category的属性 重命名categories 使用add_cate ...

  10. Qt Creator配置clang-format格式化代码插件

    clang-format是一种格式化代码的插件,可用于格式化C / C ++ / Java / JavaScript / Objective-C / Protobuf / C#代码.而Qt Creat ...