springboot — SpringApplication的生命周期和事件机制解析

13 篇文章 0 订阅
13 篇文章 0 订阅

本文将以SpringApplication的启动流程以及生命周期各时期发出的Event事件为主线,结合每个生命周期内完成的事件介绍,真正实现一文让你总览Spring Boot的全貌,这对读者深入理解Spring Boot,以及整合进Spring Cloud都将非常重要。

接下来我们先看下SpringApplication生命周期事件流程图,然后再讲解各个事件的详情:
在这里插入图片描述

1、SpringApplicationEvent

它是和SpringApplication生命周期有关的所有事件的父类,@since 1.0.0

public abstract class SpringApplicationEvent extends ApplicationEvent {
	private final String[] args;
	
	public SpringApplicationEvent(SpringApplication application, String[] args) {
		super(application);
		this.args = args;
	}
	public SpringApplication getSpringApplication() {
		return (SpringApplication) getSource();
	}
	public final String[] getArgs() {
		return this.args;
	}
}

它是抽象类,扩展自Spring Framwork的ApplicationEvent,确保了事件和应用实体SpringApplication产生关联,它有如下子类,每个事件都代表着SpringApplication不同生命周期所处的位置:

在这里插入图片描述

2、ApplicationStartingEvent

调用getRunListeners()方法, 实例化一个SpringApplicationRunListeners对象, SpringApplicationRunListeners的构造参数通过getSpringFactoriesInstances()方法获得,这个方法获取的一个EventPublishingRunListener对象, 所以调用的是EventPublishingRunListener#starting()方法

ApplicationStartingEvent构造方法中传递了一个SpringApplication对象和args参数。一直传递到了父类EventObject,将SpringApplication对象存放在source变量中。

public class SpringApplication {
    //run方法
    public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        configureHeadlessProperty();
        
        //本文分析的重点,发布启动事件ApplicationStartingEvent
        //获取SpringApplicationRunListener的子类listener
        SpringApplicationRunListeners listeners = getRunListeners(args);
        //执行其starting()方法
        listeners.starting();
        ....
    }
    
    //获取SpringApplicationRunListener的子类listener
    private SpringApplicationRunListeners getRunListeners(String[] args) {
        Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
        
        //调用getSpringFactoriesInstances方法
        //获取SpringApplicationRunListener的子类
        //子类只有一个,EventPublishingRunListener
        //实例化了一个SpringApplicationRunListeners对象
        return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
                SpringApplicationRunListener.class, types, this, args));
    }
}
2.1 SpringApplicationRunListeners

SpringApplicationRunListeners是一个比较重要的类, 之后的代码会经常调用这个方法, 内部封装了一个SpringApplicationRunListener的list, 方便以后扩展, 目前只有一个EventPublishingRunListener实例, 所以spring的事件都是由EventPublishingRunListener发布的:

//SpringApplicationRunListeners部分源码
class SpringApplicationRunListeners {
 
    private final Log log;
 
    //SpringApplicationRunListener的子类对象列表
    //listener列表扩展点
    private final List<SpringApplicationRunListener> listeners;
 
    SpringApplicationRunListeners(Log log,
            Collection<? extends SpringApplicationRunListener> listeners) {
        this.log = log;
        this.listeners = new ArrayList<>(listeners);
    }
 
    //发布启动事件
    public void starting() {
        for (SpringApplicationRunListener listener : this.listeners) {
            //目前调用EventPublishingRunListener#starting方法
            listener.starting();
        }
    }
    
    //其他事件都是相同的代码
    //....
}
2.2 EventPublishingRunListener

EventPublishingRunListener是springboot的事件广播器, 内部封装了一个SimpleApplicationEventMulticaster对象, 用来发布springboot加载过程中的各个事件

  • 事件源SpringApplication对象
  • 事件SpringApplicationEvent对象
  • 事件发布器是EventPublishingRunListener, 正在的事件发布器是其内部SimpleApplicationEventMulticaster成员变量
  • 事件监听器SpringApplication维护的listeners, 调用AbstractApplicationEventMulticaster#getApplicationListeners(ApplicationEvent, ResolvableType)筛选出支持ApplicationEvent的listeners
//EventPublishingRunListener部分源码
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
 
    //SpringApplication对象
    private final SpringApplication application;
 
    //命令函参数
    private final String[] args;
 
    //事件广播器
    private final SimpleApplicationEventMulticaster initialMulticaster;
 
    public EventPublishingRunListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
        this.initialMulticaster = new SimpleApplicationEventMulticaster();
        // 通过application.getListeners(),获取到Listener列表
        // ConfigFileApplicationListener
        // AnsiOutputApplicationListener
        // LoggingApplicationListener
        // ClasspathLoggingApplicationListener
        // BackgroundPreinitializer
        // DelegatingApplicationListener
        // ParentContextCloserApplicationListener
        // ClearCachesApplicationListener
        // FileEncodingApplicationListener
        // LiquibaseServiceLocatorApplicationListener
        for (ApplicationListener<?> listener : application.getListeners()) {
            //将listener添加到事件广播器initialMulticaster
            this.initialMulticaster.addApplicationListener(listener);
        }
    }
    @Override
    public void starting() {
        // 广播器广播ApplicationStartingEvent事件
        this.initialMulticaster.multicastEvent(
                new ApplicationStartingEvent(this.application, this.args));
    }
    
    //其他事件发布都是相同的代码
    //...
}
2.3 SimpleApplicationEventMulticaster

springboot默认事件广播器, 有三个重要方法, 用于发布spring启动过程中的各个事件:

  • addApplicationListener() 添加监听器listener
  • multicastEvent() 广播spring事件
  • invokeListener() 实现每个listener的onApplicationEvent()方法

父类AbstractApplicationEventMulticaster,封装了四个重要方法:

  • addApplicationListener添加listener
  • addApplicationListenerBean添加注入的listener bean名称
  • removeApplicationListener删除listener
  • removeApplicationListenerBean删除注入的listener bean名称
  • getApplicationListeners()先从缓存retrieverCache获取listener,如果缓存不存在, 封装数据放入缓存中, 增删listener的时候, 缓存retrieverCache会被清空

以下是父类AbstractApplicationEventMulticaster源码分析:

//抽象事件广播器
public abstract class AbstractApplicationEventMulticaster
        implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {
    
    //监听器遍历器
    //成员变量applicationListeners Set维护了application中包含的listeners,
    //成员变量applicationListenerBeans Set维护了注入的listener bean名称
    private final ListenerRetriever defaultRetriever = new ListenerRetriever(false);
 
    //调用getApplicationListeners()方法之后
    //缓存spring事件以及对应的listener列表
    final Map<ListenerCacheKey, ListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64);
 
    private Object retrievalMutex = this.defaultRetriever;
 
    @Override
    public void addApplicationListener(ApplicationListener<?> listener) {
        synchronized (this.retrievalMutex) {
            //监听器已经被加载过,
            //先执行删除操作,防止重复执行
            Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
            if (singletonTarget instanceof ApplicationListener) {
                this.defaultRetriever.applicationListeners.remove(singletonTarget);
            }
            this.defaultRetriever.applicationListeners.add(listener);
            //清空缓存
            this.retrieverCache.clear();
        }
    }
    
    /**
     * 获取支持监听event的listener
     * 这里使用了单例模式
     */
    protected Collection<ApplicationListener<?>> getApplicationListeners(
            ApplicationEvent event, ResolvableType eventType) {
 
        Object source = event.getSource();
        Class<?> sourceType = (source != null ? source.getClass() : null);
        ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
 
        //尝试从ConcurrentHashMap缓存中取出listener列表
        ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
        if (retriever != null) {
            return retriever.getApplicationListeners();
        }
 
        if (this.beanClassLoader == null ||
                (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
                        (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
            //双重检查锁定
            //当多线程访问的时候,
            //之前retriever多个线程否返回null,
            //此时锁住this.retrievalMutex
            //防止多次实例化
            synchronized (this.retrievalMutex) {
                //再尝试从cache中获取
                retriever = this.retrieverCache.get(cacheKey);
                if (retriever != null) {
                    return retriever.getApplicationListeners();
                }
                //生成Key和Value放入缓存中
                retriever = new ListenerRetriever(true);
                Collection<ApplicationListener<?>> listeners =
                        retrieveApplicationListeners(eventType, sourceType, retriever);
                this.retrieverCache.put(cacheKey, retriever);
                return listeners;
            }
        }
        else {
            //没有缓存ListenerRetriever,那么就不需要同步
            return retrieveApplicationListeners(eventType, sourceType, null);
        }
    }
    
    //retrieveApplicationListeners方法中
    //调用了supportsEvent方法
    //supportsEvent使用了适配器模式
    protected boolean supportsEvent(
            ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) {
        GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
                (GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
        return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
    }
}

SimpleApplicationEventMulticaster源码分析:
广播事件, 然后实现每个listener的onApplicationEvent()方法

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
 
    @Nullable
    private Executor taskExecutor;
 
    @Nullable
    private ErrorHandler errorHandler;
 
    /**
     * 构造函数
     * taskExecutor和errorHandler都是null
     */
    public SimpleApplicationEventMulticaster() {
    }
    
    /**
     * 广播事件
     * @param event 事件
     * @param eventType 事件类型
     */
    @Override
    public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        //调用父类getApplicationListeners方法
        //遍历所有支持ApplicationStartingEvent事件的监听器
        //LoggingApplicationListener
        //BackgroundPreinitializer
        //DelegatingApplicationListener
        //LiquibaseServiceLocatorApplicationListener
        for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            //此时的executor为null
            Executor executor = getTaskExecutor();
            if (executor != null) {
                executor.execute(() -> invokeListener(listener, event));
            }
            else {
                //调用listener
                invokeListener(listener, event);
            }
        }
    }
    
    /**
     * 具体调用监听器的方法
     * @param listener 监听器
     * @param event 事件
     */
    private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
        try {
            //调用listener的onApplicationEvent方法
            listener.onApplicationEvent(event);
        }
        catch (ClassCastException ex) {
            String msg = ex.getMessage();
            if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
                Log logger = LogFactory.getLog(getClass());
                if (logger.isDebugEnabled()) {
                    logger.debug("Non-matching event type for listener: " + listener, ex);
                }
            }
            else {
                throw ex;
            }
        }
    }
}

默认情况下,有4个监听器监听ApplicationStartingEvent事件:
在这里插入图片描述

  1. LoggingApplicationListener:日志监听器,配置日志;@since 2.0.0。对日志系统抽象LoggingSystem执行实例化以及初始化之前的操作,默认使用的是基于Logback的LogbackLoggingSystem
  2. BackgroundPreinitializer:启动一个后台进行对一些类进行预热。如ValidationInitializer、JacksonInitializer…,因为这些组件有第一次触发的特点(并且首次初始化均还比较耗时),所以使用后台线程先预热效果更佳
  3. DelegatingApplicationListener:代理监听器, 继续发布事件;它监听的是ApplicationEvent,而实际上只会ApplicationEnvironmentPreparedEvent到达时生效,所以此处忽略
  4. LiquibaseServiceLocatorApplicationListener:将liquibas替换为可以和spring配合工作的版本

总结:此事件节点结束时,SpringApplication完成了一些实例化相关的动作:本实例实例化、本实例属性赋值、日志系统实例化等。

3、ApplicationEnvironmentPreparedEvent

@since 1.0.0。该事件节点是最为重要的一个节点之一,因为对于Spring应用来说,环境抽象Enviroment简直太重要了,它是最为基础的元数据,决定着程序的构建和走向,所以构建的时机是比较早的。
完成的事件内容:

  • 封装命令行参数(main方法的args)到ApplicationArguments里面
  • 创建出一个环境抽象实例ConfigurableEnvironment的实现类,并且填入值:Profiles配置和Properties属性,默认内容如下(注意,这只是初始状态,后面还会改变):
    在这里插入图片描述
  • 发送ApplicationEnvironmentPreparedEvent事件,触发对应的监听器的执行;对环境抽象Enviroment的填值,均是由监听此事件的监听器去完成
  • bindToSpringApplication(environment):把环境属性中spring.main.xxx = xxx绑定到当前的SpringApplication实例属性上,如常用的spring.main.allow-bean-definition-overriding=true会被绑定到当前SpringApplication实例的对应属性上

监听此事件的监听器:
默认情况下,有9个监听器监听ApplicationEnvironmentPreparedEvent事件:
在这里插入图片描述

  1. BootstrapApplicationListener:来自SpringCloud。优先级最高,用于启动/创建Spring Cloud的应用上下文。需要注意的是:到此时SB的上下文ApplicationContext还并没有创建哦。这个流程“嵌套”特别像Bean初始化流程:初始化Bean A时,遇到了Bean B,就需要先去完成Bean B的初始化,再回头来继续完成Bean A的步骤。
    说明:在创建SC的应用的时候,使用的也是SpringApplication#run()完成的(非web),因此也会走下一整套SpringApplication的生命周期逻辑,所以务必区分。特别是这种case会让“绝大多数”初始化器、监听器等执行多次,若你有那种只需要执行一次的需求(比如只想让SB容器生命周期内执行,SC生命周期不执行),请务必自行处理,否则会被执行多次而带来不可预知的结果;SC应用上下文读取的外部化配置文件名默认是:bootstrap,使用的也是ConfigFileApplicationListener完成的加载/解析;

  2. LoggingSystemShutdownListener:来自SpringCloud。对LogbackLoggingSystem先清理,再重新初始化一次,效果同上个事件,相当于重新来了一次,毕竟现在有Enviroment环境;

  3. ConfigFileApplicationListener:@since 1.0.0。它也许是最重要的一个监听器。做了如下事情:

    • 加载SPI配置的所有的EnvironmentPostProcessor实例,并且排好序。需要注意的是:ConfigFileApplicationListener也是个EnvironmentPostProcessor,也会参与排序; 排好序后,分别一个个的执行以下EnvironmentPostProcessor
      在这里插入图片描述
  4. AnsiOutputApplicationListener:@since 1.2.0。让你的终端(可以是控制台、可以是日志文件)支持Ansi彩色输出,使其更具可读性。当然前提是你的终端支持ANSI显示。参考类:AnsiOutput。你可通过spring.output.ansi.enabled = xxx配置,可选值是:DETECT/ALWAYS/NEVER,一般不动即可。另外,针对控制台可以单独配置:spring.output.ansi.console-available = true/false

  5. LoggingApplicationListener:@since 2.0.0。根据Enviroment环境完成initialize()初始化动作:日志等级、日志格式模版等;
    值得注意的是:它这步相当于在ApplicationStartingEvent事件基础上进一步完成了初始化(上一步只是实例化);

  6. ClasspathLoggingApplicationListener:@since 2.0.0。用于把classpath路径以log.debug()输出;
    值得注意的是:classpath类路径是有N多个的Arrays.toString(((URLClassLoader) classLoader).getURLs()),也就是说每个.jar里都属于classpath的范畴;

  7. BackgroundPreinitializer:本事件达到时无动作;

  8. DelegatingApplicationListener:执行通过外部化配置context.listener.classes = xxx,xxx的监听器们,然后把该事件广播给他们,执行监听此事件的监听器;
    这麽做的好处:可以通过属性文件外部化配置监听器,而不一定必须写在spring.factories里,更具弹性;

  9. FileEncodingApplicationListener:检测当前系统环境的file.encoding和spring.mandatory-file-encoding设置的值是否一样,如果不一样则抛出异常如果不配置spring.mandatory-file-encoding则不检查;

总结:此事件节点结束时,Spring Boot的环境抽象Enviroment已经准备完毕,但此时其上下文ApplicationContext还没有创建,但是Spring Cloud的应用上下文(引导上下文)已经全部初始化完毕哦,所以SC管理的外部化配置也应该都进入到了SB里面。如下图所示(这是基本上算是Enviroment的最终态了):
在这里插入图片描述

4、ApplicationContextInitializedEvent

@since 2.1.0,非常新的一个事件。当SpringApplication的上下文ApplicationContext准备好后,对单例Bean们实例化之前,发送此事件。所以此事件又可称为:contextPrepared事件。
完成的事件内容:
上下文实例已经有了,那么就开始对它进行一些参数的设置:

  • 首先最重要的便是把已经准备好的环境Enviroment环境设置给它
  • 设置些beanNameGenerator、resourceLoader、ConversionService等组件
  • 实例化所有的ApplicationContextInitializer上下文初始化器,并且排序好后挨个执行它(这个很重要),默认有如下截图这些初始化器此时要执行:
    在这里插入图片描述
    监听此事件的监听器:
    监听此事件默认情况下,有2个监听器监听ApplicationContextInitializedEvent事件:
    在这里插入图片描述
  • BackgroundPreinitializer:本事件达到时无动作
  • DelegatingApplicationListener:本事件达到时无动作

总结:此事件节点结束时,完成了应用上下文ApplicationContext的准备工作,并且执行所有注册的上下文初始化器ApplicationContextInitializer。但是此时,单例Bean是仍旧还没有初始化,并且WebServer也还没有启动;

5、ApplicationPreparedEvent

@since 1.0.0。截止到上个事件ApplicationContextInitializedEvent,应用上下文ApplicationContext充其量叫实例化好了,但是还剩下很重要的事没做,便是此事件的执行内容了;
完成的事件内容:

  • 把applicationArguments、printedBanner等都作为一个Bean放进Bean工厂里(因此你就可以@Autowired注入的哦)
    比如:有了Banner这个Bean,你可以在你任何想要输出的地方输出一个Banner,而不仅仅是启动时只会输出一次了;
  • 若lazyInitialization = true延迟初始化,那就向Bean工厂放一个:new LazyInitializationBeanFactoryPostProcessor()
    该处理器@since 2.2.0。该处理器的作用是:对所有的Bean(通过LazyInitializationExcludeFilter接口指定的排除在外)全部.setLazyInit(true),延迟初始化;
  • 根据primarySources和allSources,交给BeanDefinitionLoader(SB提供的实现)实现加载Bean的定义信息,它支持4种加载方式(4种源):
    AnnotatedBeanDefinitionReader -> 基于注解
    XmlBeanDefinitionReader -> 基于xml配置
    GroovyBeanDefinitionReader -> Groovy文件
    ClassPathBeanDefinitionScanner -> classpath中加载(不同的源使用了不同的load加载方式)
  • 发送ApplicationPreparedEvent事件,触发对应的监听器的执行

总结:此事件节点结束时,应用上下文ApplicationContext初始化完成,该赋值的赋值了,Bean定义信息也已全部加载完成。但是,单例Bean还没有被实例化,web容器依旧还没启动。

6、ApplicationStartedEvent

@since 2.0.0。截止到此,应用已经准备就绪,并且通过监听器、初始化器等完成了非常多的工作了,但仍旧剩下被认为最为重要的初始化单例Bean动作还没做、web容器(如Tomcat)还没启动,这便是这个周期所要做的事。
完成的事件内容:

  • 启动Spring容器:AbstractApplicationContext#refresh(),这个步骤会做很多事,比如会实例化单例Bean;该步骤属于Spring Framework的核心内容范畴,做了很多事,请参考Spring核心技术;在Spring容器refresh()启动完成后,WebServer也随之完成启动,成功监听到对应端口;
  • 输出启动成功的日志:Started Application in xxx seconds (JVM running for xxx);
  • 发送ApplicationStartedEvent事件,触发对应的监听器的执行;
  • callRunners():依次执行容器内配置的ApplicationRunner/CommandLineRunner的Bean实现类,支持sort排序:
    ApplicationRunner:@since 1.3.0,入参是ApplicationArguments,先执行(推荐使用)
    CommandLineRunner:@since 1.0.0,入参是String… args,后执行(不推荐使用)

监听此事件的监听器:
默认情况下,有3个监听器监听ApplicationStartedEvent事件:
在这里插入图片描述

  1. 前两个不用再解释了吧:本事件达到时无动作
  2. TomcatMetricsBinder:@since 2.1.0。和监控相关:将你的tomcat指标信息TomcatMetrics绑定到MeterRegistry,从而就能收集到相关指标了

总结:此事件节点结束时,SpringApplication的生命周期到这一步,正常的启动流程就全部完成了。也就说Spring Boot应用可以正常对对外提供服务了。

7、ApplicationReadyEvent

@since 1.3.0。该事件所处的生命周期可认为基本和ApplicationStartedEvent相同,仅是在其后执行而已,两者中间并无其它特别的动作,但是监听此事件的监听器们还是蛮重要的。
监听此事件的监听器:
默认情况下,有4个监听器监听ApplicationStartedEvent事件:
在这里插入图片描述

  1. SpringApplicationAdminMXBeanRegistrar:当此事件到达时,告诉Admin Spring应用已经ready,可以使用啦。
  2. 中间这两个不用再解释了吧:本事件达到时无动作
  3. RefreshEventListener:当此事件到达时,告诉Spring应用已经ready了,接下来便可以执行ContextRefresher.refresh()

总结:此事件节点结束时,应用已经完完全全的准备好了,并且也已经完成了相关组件的周知工作。

8、ApplicationFailedEvent

当SpringApplication在启动时抛出异常:可能是端口绑定、也可能是你自定义的监听器你写了个bug等,就会“可能”发送此事件。

完成的事件内容:

  1. 得到异常的退出码ExitCode,然后发送ExitCodeEvent事件(非生命周期事件)
  2. 发送ApplicationFailedEvent事件,触发对应的监听器的执行

监听此事件的监听器:
默认情况下,有6个监听器监听ApplicationStartedEvent事件:
在这里插入图片描述

  1. LoggingApplicationListener:执行loggingSystem.cleanUp()清理资源
  2. ClasspathLoggingApplicationListener:输出一句debug日志:Application failed to start with classpath: …
  3. 中间这两个不用再解释了吧:本事件达到时无动作
  4. ConditionEvaluationReportLoggingListener:自动配置输出报告,输出错误日志呗:特别方便你查看和错误定位;
  5. BootstrapApplicationListener.CloseContextOnFailureApplicationListener:执行context.close()
    总结:此事件节点结束时,会做一些释放资源的操作。一般情况下:我们并不需要监听到此事件;
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RachelHwang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值