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

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

SpringBoot中文注释项目Github地址:

https://github.com/yuanmabiji/spring-boot-2.1.0.RELEASE

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

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

为广播SpringBoot内置生命周期事件做前期准备:1)首先加载ApplicationListener监听器实现类;2)其次加载SPI扩展类EventPublishingRunListener。
SpringBoot启动时利用EventPublishingRunListener广播生命周期事件,然后ApplicationListener监听器实现类监听相应的生命周期事件执行一些初始化逻辑的工作。
2 引言
上篇文章的侧重点是分析了SpringBoot启动时广播生命周期事件的原理,此篇文章我们再来详细分析SpringBoot内置的7种生命周期事件的源码。

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

由上图可以看到事件类之间的关系:
最顶级的父类是JDK的事件基类EventObject;
然后Spring的事件基类ApplicationEvent继承了JDK的事件基类EventObject;
其次SpringBoot的生命周期事件基类SpringApplicationEvent继承了Spring的事件基类ApplicationEvent;
最后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的具体事件类继承,源码如下:

// 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属性一般有如下作用:

从事件中取出context属性,然后可以增加一些后置处理器,比如ConfigFileApplicationListener监听器监听到ApplicationPreparedEvent事件后,然后取出context变量,通过context变量增加了PropertySourceOrderingPostProcessor这个后置处理器;
通过context属性取出beanFactory容器,然后注册一些bean,比如LoggingApplicationListener监听器通过ApplicationPreparedEvent事件的context属性取出beanFactory容器,然后注册了springBootLoggingSystem这个单例bean;
通过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事件将在容器刷新后但ApplicationRunner和CommandLineRunner的run方法执行前触发,标志Spring容器已经刷新,此时容器已经准备完毕了。

扩展: 这里提到了ApplicationRunner和CommandLineRunner接口有啥作用呢?我们一般会在Spring容器刷新完毕后,此时可能有一些系统参数等静态数据需要加载,此时我们就可以实现了ApplicationRunner或CommandLineRunner接口来实现静态数据的加载。

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事件在调用完ApplicationRunner和CommandLineRunner的run方法后触发,此时标志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-SourceCode-Blogs

点赞搞起来,嘿嘿嘿!

相关文章
|
前端开发 Java 关系型数据库
基于Java+Springboot+Vue开发的鲜花商城管理系统源码+运行
基于Java+Springboot+Vue开发的鲜花商城管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的鲜花商城管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。技术学习共同进步
805 7
|
JavaScript 前端开发 Java
制造业ERP源码,工厂ERP管理系统,前端框架:Vue,后端框架:SpringBoot
这是一套基于SpringBoot+Vue技术栈开发的ERP企业管理系统,采用Java语言与vscode工具。系统涵盖采购/销售、出入库、生产、品质管理等功能,整合客户与供应商数据,支持在线协同和业务全流程管控。同时提供主数据管理、权限控制、工作流审批、报表自定义及打印、在线报表开发和自定义表单功能,助力企业实现高效自动化管理,并通过UniAPP实现移动端支持,满足多场景应用需求。
1189 1
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
720 70
|
供应链 JavaScript BI
ERP系统源码,基于SpringBoot+Vue+ElementUI+UniAPP开发
这是一款专为小微企业打造的 SaaS ERP 管理系统,基于 SpringBoot+Vue+ElementUI+UniAPP 技术栈开发,帮助企业轻松上云。系统覆盖进销存、采购、销售、生产、财务、品质、OA 办公及 CRM 等核心功能,业务流程清晰且操作简便。支持二次开发与商用,提供自定义界面、审批流配置及灵活报表设计,助力企业高效管理与数字化转型。
897 2
ERP系统源码,基于SpringBoot+Vue+ElementUI+UniAPP开发
|
12月前
|
机器学习/深度学习 数据采集 人机交互
springboot+redis互联网医院智能导诊系统源码,基于医疗大模型、知识图谱、人机交互方式实现
智能导诊系统基于医疗大模型、知识图谱与人机交互技术,解决患者“知症不知病”“挂错号”等问题。通过多模态交互(语音、文字、图片等)收集病情信息,结合医学知识图谱和深度推理,实现精准的科室推荐和分级诊疗引导。系统支持基于规则模板和数据模型两种开发原理:前者依赖人工设定症状-科室规则,后者通过机器学习或深度学习分析问诊数据。其特点包括快速病情收集、智能病症关联推理、最佳就医推荐、分级导流以及与院内平台联动,提升患者就诊效率和服务体验。技术架构采用 SpringBoot+Redis+MyBatis Plus+MySQL+RocketMQ,确保高效稳定运行。
832 0
|
小程序 Java 关系型数据库
weixin117新闻资讯系统设计+springboot(文档+源码)_kaic
本文介绍了一款基于微信小程序的新闻资讯系统,涵盖其开发全过程。该系统采用Java的SSM框架进行后台管理开发,使用MySQL作为本地数据库,并借助微信开发者工具确保稳定性。管理员可通过个人中心、用户管理等功能模块实现高效管理,而用户则能注册登录并查看新闻与视频内容。系统设计注重可行性分析(技术、经济、操作),强调安全性与数据完整性,界面简洁易用,功能全面,极大提升了信息管理效率及用户体验。关键词包括基于微信小程序的新闻资讯系统、SSM框架和MYSQL数据库。
|
小程序 JavaScript Java
基于SpringBoot的智慧停车场微信小程序源码分享
智慧停车场微信小程序主要包含管理端和小程序端。管理端包括停车场管理,公告信息管理,用户信息管理,预定信息管理,用户反馈管理等功能。小程序端包括登录注册,预约停车位,停车导航,停车缴费,用户信息,车辆信息,钱包充值,意见反馈等功能。
961 5
基于SpringBoot的智慧停车场微信小程序源码分享
基于springboot+thymeleaf+Redis仿知乎网站问答项目源码
基于springboot+thymeleaf+Redis仿知乎网站问答项目源码
417 36
|
Java 开发者 Spring
java springboot监听事件和处理事件
通过上述步骤,开发者可以在Spring Boot项目中轻松实现事件的发布和监听。事件机制不仅解耦了业务逻辑,还提高了系统的可维护性和扩展性。掌握这一技术,可以显著提升开发效率和代码质量。
442 33
|
JavaScript Java 测试技术
基于SpringBoot+Vue实现的留守儿童爱心网站设计与实现(计算机毕设项目实战+源码+文档)
博主是一位全网粉丝超过100万的CSDN特邀作者、博客专家,专注于Java、Python、PHP等技术领域。提供SpringBoot、Vue、HTML、Uniapp、PHP、Python、NodeJS、爬虫、数据可视化等技术服务,涵盖免费选题、功能设计、开题报告、论文辅导、答辩PPT等。系统采用SpringBoot后端框架和Vue前端框架,确保高效开发与良好用户体验。所有代码由博主亲自开发,并提供全程录音录屏讲解服务,保障学习效果。欢迎点赞、收藏、关注、评论,获取更多精品案例源码。