SpringBoot内置生命周期事件详解 SpringBoot源码(十)

SpringBoot中文注释项目Github地址:java

https://github.com/yuanmabiji...git

本篇接 SpringBoot事件监听机制源码分析(上) SpringBoot源码(九)github

1 温故而知新

温故而知新,咱们来简单回顾一下上篇的内容,上一篇咱们分析了SpringBoot启动时广播生命周期事件的原理,现将关键步骤再浓缩总结下:spring

  1. 为广播SpringBoot内置生命周期事件作前期准备:1)首先加载ApplicationListener监听器实现类;2)其次加载SPI扩展类EventPublishingRunListener
  2. SpringBoot启动时利用EventPublishingRunListener广播生命周期事件,而后ApplicationListener监听器实现类监听相应的生命周期事件执行一些初始化逻辑的工做。

2 引言

上篇文章的侧重点是分析了SpringBoot启动时广播生命周期事件的原理,此篇文章咱们再来详细分析SpringBoot内置的7种生命周期事件的源码。后端

3 SpringBoot生命周期事件源码分析

分析SpringBoot的生命周期事件,咱们先来看一张类结构图:

由上图能够看到事件类之间的关系:app

  1. 最顶级的父类是JDK的事件基类EventObject
  2. 而后Spring的事件基类ApplicationEvent继承了JDK的事件基类EventObject
  3. 其次SpringBoot的生命周期事件基类SpringApplicationEvent继承了Spring的事件基类ApplicationEvent
  4. 最后SpringBoot具体的7个生命周期事件类再继承了SpringBoot的生命周期事件基类SpringApplicationEvent

3.1 JDK的事件基类EventObject

EventObject类是JDK的事件基类,能够说是全部Java事件类的基本,即全部的Java事件类都直接或间接继承于该类,源码以下:框架

// EventObject.java

public class EventObject implements java.io.Serializable {

    private static final long serialVersionUID = 5516075349620653480L;

    /**
     * The object on which the Event initially occurred.
     */
    protected transient Object  source;
    /**
     * Constructs a prototypical Event.
     *
     * @param    source    The object on which the Event initially occurred.
     * @exception  IllegalArgumentException  if source is null.
     */
    public EventObject(Object source) {
        if (source == null)
            throw new IllegalArgumentException("null source");
        this.source = source;
    }
    /**
     * The object on which the Event initially occurred.
     *
     * @return   The object on which the Event initially occurred.
     */
    public Object getSource() {
        return source;
    }
    /**
     * Returns a String representation of this EventObject.
     *
     * @return  A a String representation of this EventObject.
     */
    public String toString() {
        return getClass().getName() + "[source=" + source + "]";
    }
}

能够看到EventObject类只有一个属性source,这个属性是用来记录最初事件是发生在哪一个类,举个栗子,好比在SpringBoot启动过程当中会发射ApplicationStartingEvent事件,而这个事件最初是在SpringApplication类中发射的,所以source就是SpringApplication对象。函数

3.2 Spring的事件基类ApplicationEvent

ApplicationEvent继承了DK的事件基类EventObject类,是Spring的事件基类,被全部Spring的具体事件类继承,源码以下:spring-boot

// ApplicationEvent.java

/**
 * Class to be extended by all application events. Abstract as it
 * doesn't make sense for generic events to be published directly.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 */
public abstract class ApplicationEvent extends EventObject {
    /** use serialVersionUID from Spring 1.2 for interoperability. */
    private static final long serialVersionUID = 7099057708183571937L;
    /** System time when the event happened. */
    private final long timestamp;
    /**
     * Create a new ApplicationEvent.
     * @param source the object on which the event initially occurred (never {@code null})
     */
    public ApplicationEvent(Object source) {
        super(source);
        this.timestamp = System.currentTimeMillis();
    }
    /**
     * Return the system time in milliseconds when the event happened.
     */
    public final long getTimestamp() {
        return this.timestamp;
    }
}

能够看到ApplicationEvent有且仅有一个属性timestamp,该属性是用来记录事件发生的时间。源码分析

3.3 SpringBoot的事件基类SpringApplicationEvent

SpringApplicationEvent类继承了Spring的事件基类ApplicationEvent,是全部SpringBoot内置生命周期事件的父类,源码以下:

/**
 * Base class for {@link ApplicationEvent} related to a {@link SpringApplication}.
 *
 * @author Phillip Webb
 */
@SuppressWarnings("serial")
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;
    }
}

能够看到SpringApplicationEvent有且仅有一个属性args,该属性就是SpringBoot启动时的命令行参数即标注@SpringBootApplication启动类中main函数的参数。

3.4 SpringBoot具体的生命周期事件类

接下来咱们再来看一下SpringBoot内置生命周期事件即SpringApplicationEvent的具体子类们。

3.4.1 ApplicationStartingEvent

// ApplicationStartingEvent.java

public class ApplicationStartingEvent extends SpringApplicationEvent {
    public ApplicationStartingEvent(SpringApplication application, String[] args) {
        super(application, args);
    }
}

SpringBoot开始启动时便会发布ApplicationStartingEvent事件,其发布时机在环境变量Environment或容器ApplicationContext建立前但在注册ApplicationListener具体监听器以后,标志标志SpringApplication开始启动。

3.4.2 ApplicationEnvironmentPreparedEvent

// ApplicationEnvironmentPreparedEvent.java

public class ApplicationEnvironmentPreparedEvent extends SpringApplicationEvent {
    private final ConfigurableEnvironment environment;
    /**
     * Create a new {@link ApplicationEnvironmentPreparedEvent} instance.
     * @param application the current application
     * @param args the arguments the application is running with
     * @param environment the environment that was just created
     */
    public ApplicationEnvironmentPreparedEvent(SpringApplication application,
            String[] args, ConfigurableEnvironment environment) {
        super(application, args);
        this.environment = environment;
    }
    /**
     * Return the environment.
     * @return the environment
     */
    public ConfigurableEnvironment getEnvironment() {
        return this.environment;
    }
}

能够看到ApplicationEnvironmentPreparedEvent事件多了一个environment属性,咱们不妨想一下,多了environment属性的做用是啥?
答案就是ApplicationEnvironmentPreparedEvent事件的environment属性做用是利用事件发布订阅机制,相应监听器们能够从ApplicationEnvironmentPreparedEvent事件中取出environment变量,而后咱们能够为environment属性增长属性值或读出environment变量中的值。

举个栗子: ConfigFileApplicationListener监听器就是监听了 ApplicationEnvironmentPreparedEvent事件,而后取出 ApplicationEnvironmentPreparedEvent事件的 environment属性,而后再为 environment属性增长 application.properties配置文件中的环境变量值。

当SpringApplication已经开始启动且环境变量Environment已经建立后,而且为环境变量Environment配置了命令行和Servlet等类型的环境变量后,此时会发布ApplicationEnvironmentPreparedEvent事件。

监听ApplicationEnvironmentPreparedEvent事件的第一个监听器是ConfigFileApplicationListener,由于是ConfigFileApplicationListener监听器还要为环境变量Environment增长application.properties配置文件中的环境变量;此后还有一些也是监听ApplicationEnvironmentPreparedEvent事件的其余监听器监听到此事件时,此时能够说环境变量Environment几乎已经彻底准备好了。

思考: 监听同一事件的监听器们执行监听逻辑时是有顺序的,咱们能够想一下这个排序逻辑是何时排序的?还有为何要这样排序呢?

3.4.3 ApplicationContextInitializedEvent

// ApplicationContextInitializedEvent.java

public class ApplicationContextInitializedEvent extends SpringApplicationEvent {
    private final ConfigurableApplicationContext context;
    /**
     * Create a new {@link ApplicationContextInitializedEvent} instance.
     * @param application the current application
     * @param args the arguments the application is running with
     * @param context the context that has been initialized
     */
    public ApplicationContextInitializedEvent(SpringApplication application,
            String[] args, ConfigurableApplicationContext context) {
        super(application, args);
        this.context = context;
    }
    /**
     * Return the application context.
     * @return the context
     */
    public ConfigurableApplicationContext getApplicationContext() {
        return this.context;
    }
}

能够看到ApplicationContextInitializedEvent事件多了个ConfigurableApplicationContext类型的context属性,context属性的做用一样是为了相应监听器能够拿到这个context属性执行一些逻辑,具体做用将在3.4.4详述。

ApplicationContextInitializedEvent事件在ApplicationContext容器建立后,且为ApplicationContext容器设置了environment变量和执行了ApplicationContextInitializers的初始化方法后但在bean定义加载前触发,标志ApplicationContext已经初始化完毕。

扩展: 能够看到 ApplicationContextInitializedEvent是在为 context容器配置 environment变量后触发,此时 ApplicationContextInitializedEvent等事件只要有 context容器的话,那么其余须要 environment环境变量的监听器只须要从 context中取出 environment变量便可,从而 ApplicationContextInitializedEvent等事件不必再配置 environment属性。

3.4.4 ApplicationPreparedEvent

// ApplicationPreparedEvent.java

public class ApplicationPreparedEvent extends SpringApplicationEvent {
    private final ConfigurableApplicationContext context;
    /**
     * Create a new {@link ApplicationPreparedEvent} instance.
     * @param application the current application
     * @param args the arguments the application is running with
     * @param context the ApplicationContext about to be refreshed
     */
    public ApplicationPreparedEvent(SpringApplication application, String[] args,
            ConfigurableApplicationContext context) {
        super(application, args);
        this.context = context;
    }
    /**
     * Return the application context.
     * @return the context
     */
    public ConfigurableApplicationContext getApplicationContext() {
        return this.context;
    }
}

一样能够看到ApplicationPreparedEvent事件多了个ConfigurableApplicationContext类型的context属性,多了context属性的做用是能让监听该事件的监听器们能拿到context属性,监听器拿到context属性通常有以下做用:

  1. 从事件中取出context属性,而后能够增长一些后置处理器,好比ConfigFileApplicationListener监听器监听到ApplicationPreparedEvent事件后,而后取出context变量,经过context变量增长了PropertySourceOrderingPostProcessor这个后置处理器;
  2. 经过context属性取出beanFactory容器,而后注册一些bean,好比LoggingApplicationListener监听器经过ApplicationPreparedEvent事件的context属性取出beanFactory容器,而后注册了springBootLoggingSystem这个单例bean
  3. 经过context属性取出Environment环境变量,而后就能够操做环境变量,好比PropertiesMigrationListener

ApplicationPreparedEvent事件在ApplicationContext容器已经彻底准备好时但在容器刷新前触发,在这个阶段bean定义已经加载完毕还有environment已经准备好能够用了。

3.4.5 ApplicationStartedEvent

// ApplicationStartedEvent.java

public class ApplicationStartedEvent extends SpringApplicationEvent {
    private final ConfigurableApplicationContext context;
    /**
     * Create a new {@link ApplicationStartedEvent} instance.
     * @param application the current application
     * @param args the arguments the application is running with
     * @param context the context that was being created
     */
    public ApplicationStartedEvent(SpringApplication application, String[] args,
            ConfigurableApplicationContext context) {
        super(application, args);
        this.context = context;
    }
    /**
     * Return the application context.
     * @return the context
     */
    public ConfigurableApplicationContext getApplicationContext() {
        return this.context;
    }
}

ApplicationStartedEvent事件将在容器刷新后但ApplicationRunnerCommandLineRunnerrun方法执行前触发,标志Spring容器已经刷新,此时容器已经准备完毕了。

扩展: 这里提到了 ApplicationRunnerCommandLineRunner接口有啥做用呢?咱们通常会在 Spring容器刷新完毕后,此时可能有一些系统参数等静态数据须要加载,此时咱们就能够实现了 ApplicationRunnerCommandLineRunner接口来实现静态数据的加载。

3.4.6 ApplicationReadyEvent

// ApplicationReadyEvent.java

public class ApplicationReadyEvent extends SpringApplicationEvent {
    private final ConfigurableApplicationContext context;
    /**
     * Create a new {@link ApplicationReadyEvent} instance.
     * @param application the current application
     * @param args the arguments the application is running with
     * @param context the context that was being created
     */
    public ApplicationReadyEvent(SpringApplication application, String[] args,
            ConfigurableApplicationContext context) {
        super(application, args);
        this.context = context;
    }
    /**
     * Return the application context.
     * @return the context
     */
    public ConfigurableApplicationContext getApplicationContext() {
        return this.context;
    }
}

ApplicationReadyEvent事件在调用完ApplicationRunnerCommandLineRunnerrun方法后触发,此时标志SpringApplication已经正在运行。

3.4.7 ApplicationFailedEvent

// ApplicationFailedEvent.java

public class ApplicationFailedEvent extends SpringApplicationEvent {
    private final ConfigurableApplicationContext context;
    private final Throwable exception;
    /**
     * Create a new {@link ApplicationFailedEvent} instance.
     * @param application the current application
     * @param args the arguments the application was running with
     * @param context the context that was being created (maybe null)
     * @param exception the exception that caused the error
     */
    public ApplicationFailedEvent(SpringApplication application, String[] args,
            ConfigurableApplicationContext context, Throwable exception) {
        super(application, args);
        this.context = context;
        this.exception = exception;
    }
    /**
     * Return the application context.
     * @return the context
     */
    public ConfigurableApplicationContext getApplicationContext() {
        return this.context;
    }
    /**
     * Return the exception that caused the failure.
     * @return the exception
     */
    public Throwable getException() {
        return this.exception;
    }
}

能够看到ApplicationFailedEvent事件除了多了一个context属性外,还多了一个Throwable类型的exception属性用来记录SpringBoot启动失败时的异常。

ApplicationFailedEvent事件在SpringBoot启动失败时触发,标志SpringBoot启动失败。

4 小结

此篇文章相对简单,对SpringBoot内置的7种生命周期事件进行了详细分析。咱们仍是引用上篇文章的一张图来回顾一下这些生命周期事件及其用途:

5 写在最后

因为有一些小伙伴们建议以前有些源码分析文章太长,致使耐心不够,看不下去,所以,以后的源码分析文章若是太长的话,笔者将会考虑拆分为几篇文章,这样就比较短小了,比较容易看完,嘿嘿。

【源码笔记】Github地址:

https://github.com/yuanmabiji...

点赞搞起来,嘿嘿嘿!


公众号【源码笔记】专一于Java后端系列框架的源码分析。

相关文章
相关标签/搜索