事件和监听器

生命周期监听

场景:监听应用的生命周期

监听器-SpringApplicationRunListener

  1. 自定义SpringApplicationRunListener来监听事件;

    1.1. 编写SpringApplicationRunListener 实现类

    1.2. 在 META-INF/spring.factories 中配置 org.springframework.boot.SpringApplicationRunListener=自己的Listener,还可以指定一个有参构造器,接受两个参数(SpringApplication application, String[] args)

    1.3. springboot 在spring-boot.jar中配置了默认的 Listener,如下

org\springframework\boot\spring-boot\3.1.5\spring-boot-3.1.5.jar!\META-INF\spring.factories

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
/**
* Listener先要从 META-INF/spring.factories 读到
*
* 1、引导: 利用 BootstrapContext 引导整个项目启动
* starting: 应用开始,SpringApplication的run方法一调用,只要有了 BootstrapContext 就执行
* environmentPrepared: 环境准备好(把启动参数等绑定到环境变量中),但是ioc还没有创建;【调一次】
* 2、启动:
* contextPrepared: ioc容器创建并准备好,但是sources(主配置类)没加载。并关闭引导上下文;组件都没创建 【调一次】
* contextLoaded: ioc容器加载。主配置类加载进去了。但是ioc容器还没刷新(我们的bean没创建)。
* =======截止以前,ioc容器里面还没造bean呢=======
* started: ioc容器刷新了(所有bean造好了),但是 runner 没调用。
* ready: ioc容器刷新了(所有bean造好了),所有 runner 调用完了。
* 3、运行
* 以前步骤都正确执行,代表容器running。
*/

生命周期全流程

事件触发时机

各种回调监听器

  • BootstrapRegistryInitializer: 感知特定阶段:感知引导初始化

    • META-INF/spring.factories
    • 创建引导上下文bootstrapContext的时候触发。
    • application.addBootstrapRegistryInitializer();
    • 场景:进行密钥校对授权。
  • ApplicationContextInitializer: 感知特定阶段: 感知ioc容器初始化
    • META-INF/spring.factories
    • application.addInitializers();
  • ApplicationListener: 感知全阶段:基于事件机制,感知事件。 一旦到了哪个阶段可以做别的事
    • @Bean或@EventListener: 事件驱动
    • SpringApplication.addListeners(…)SpringApplicationBuilder.listeners(…)
    • META-INF/spring.factories
  • SpringApplicationRunListener: 感知全阶段生命周期 + 各种阶段都能自定义操作; 功能更完善。
    • META-INF/spring.factories
  • ApplicationRunner: 感知特定阶段:感知应用就绪Ready。卡死应用,就不会就绪
    • @Bean
  • CommandLineRunner: 感知特定阶段:感知应用就绪Ready。卡死应用,就不会就绪
    • @Bean

最佳实战:

  • 如果项目启动前做事: BootstrapRegistryInitializer 和 ApplicationContextInitializer
  • 如果想要在项目启动完成后做事:ApplicationRunner和 CommandLineRunner
  • 如果要干涉生命周期做事:SpringApplicationRunListener
  • 如果想要用事件机制:ApplicationListener

完整触发流程

9大事件触发顺序&时机

  1. ApplicationStartingEvent:应用启动但未做任何事情, 除过注册listeners and initializers.
  2. ApplicationEnvironmentPreparedEvent: Environment 准备好,但context 未创建.
  3. ApplicationContextInitializedEvent: ApplicationContext 准备好,ApplicationContextInitializers 调用,但是任何bean未加载
  4. ApplicationPreparedEvent: 容器刷新之前,bean定义信息加载
  5. ApplicationStartedEvent: 容器刷新完成, runner未调用

    ---以下就开始插入了探针机制---
  6. AvailabilityChangeEventLivenessState.CORRECT应用存活; 存活探针
  7. ApplicationReadyEvent: 任何runner被调用
  8. AvailabilityChangeEventReadinessState.ACCEPTING_TRAFFIC就绪探针,可以接请求
  9. ApplicationFailedEvent :启动出错

应用事件发送顺序如下:



感知应用是否存活了:可能植物状态,虽然活着但是不能处理请求。

应用是否就绪了:能响应请求,说明确实活的比较好。

代码使用SpringApplicationRunListenerApplicationListener

/resource/META-INF/spring.factories

org.springframework.boot.SpringApplicationRunListener=\
com.atguigu.boot3.core.listener.MyApplicationListener org.springframework.context.ApplicationListener=\
com.atguigu.boot3.core.listener.MyEventListener

MyApplicationListener.java

用来感知SpringBoot生命周期

package com.atguigu.boot3.core.listener;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment; import java.time.Duration; /**
* SpringBoot的生命周期
*/
@Slf4j
public class MyApplicationListener implements SpringApplicationRunListener { /**
* 在run方法首次启动时立即调用。可以用于非常早期的初始化。
*/
@Override
public void starting(ConfigurableBootstrapContext bootstrapContext) {
System.out.println("***starting***");
} /**
* 环境准备好就会调用,但是在 ApplicationContext 创建前
*/
@Override
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
System.out.println("***environmentPrepared***");
} /**
* ApplicationContext已经被创建,但是还未加载
*/
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("***contextPrepared***");
} /**
* 在ApplicationContext加载后但在刷新之前调用
*/
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("***contextLoaded***");
} /**
* ApplicationContext已经刷新,但是CommandLineRunners和ApplicationRunners还未被调用
*/
@Override
public void started(ConfigurableApplicationContext context, Duration timeTaken) {
System.out.println("***started***");
} /**
* 在调用了CommandLineRunners和ApplicationRunners后,ApplicationContext容器run方法运行之前运行
*/
@Override
public void ready(ConfigurableApplicationContext context, Duration timeTaken) {
System.out.println("***ready***");
} /**
* 在应用程序运行出错时运行
*/
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
System.out.println("***failed***");
}
}

MyEventListener.java

监听事件

package com.atguigu.boot3.core.listener;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener; public class MyEventListener implements ApplicationListener<ApplicationEvent> { @Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("--[event]--" + event.getClass().getName());
}
}

运行结果

E:\Java\jdk-17.0.5\bin\java.exe ...

--[event]--org.springframework.boot.context.event.ApplicationStartingEvent
***starting***
--[event]--org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent
***environmentPrepared*** . ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.1.5) --[event]--org.springframework.boot.context.event.ApplicationContextInitializedEvent
***contextPrepared***
2023-10-30T18:08:56.239+08:00 INFO 16448 --- [ main] com.atguigu.boot3.core.MainApplication : Starting MainApplication using Java 17.0.5 with PID 16448 (E:\code\IdeaProjects\spring-boot-3\boot3-07-core\target\classes started by 朱俊伟 in E:\code\IdeaProjects\spring-boot-3)
2023-10-30T18:08:56.242+08:00 INFO 16448 --- [ main] com.atguigu.boot3.core.MainApplication : The following 1 profile is active: "dev"
--[event]--org.springframework.boot.context.event.ApplicationPreparedEvent
***contextLoaded***
2023-10-30T18:08:57.269+08:00 INFO 16448 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2023-10-30T18:08:57.281+08:00 INFO 16448 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2023-10-30T18:08:57.281+08:00 INFO 16448 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.15]
2023-10-30T18:08:57.367+08:00 INFO 16448 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2023-10-30T18:08:57.368+08:00 INFO 16448 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1064 ms
2023-10-30T18:08:57.765+08:00 INFO 16448 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
--[event]--org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent
--[event]--org.springframework.context.event.ContextRefreshedEvent
2023-10-30T18:08:57.774+08:00 INFO 16448 --- [ main] com.atguigu.boot3.core.MainApplication : Started MainApplication in 2.248 seconds (process running for 2.97)
--[event]--org.springframework.boot.context.event.ApplicationStartedEvent
--[event]--org.springframework.boot.availability.AvailabilityChangeEvent
***started***
--[event]--org.springframework.boot.context.event.ApplicationReadyEvent
--[event]--org.springframework.boot.availability.AvailabilityChangeEvent
***ready***
--[event]--org.springframework.boot.availability.AvailabilityChangeEvent
--[event]--org.springframework.context.event.ContextClosedEvent

参考:

https://www.yuque.com/leifengyang/springboot3/lliphvul8b19pqxp#beI2B

SpringBoot事件和监听器的更多相关文章

  1. SpringBoot事件监听器源码分析

    本文涉及到Spring的监听器,如果不太了解请先阅读之前的Spring监听器的文章. SpringBoot事件监听器初始化 SpringBoot中默认定义了11个事件监听器对象,全部定义在META-I ...

  2. SpringBoot事件监听机制源码分析(上) SpringBoot源码(九)

    SpringBoot中文注释项目Github地址: https://github.com/yuanmabiji/spring-boot-2.1.0.RELEASE 本篇接 SpringApplicat ...

  3. Spring Boot 启动事件和监听器,太强大了!

    大家都知道,在 Spring 框架中事件和监听无处不在,打通了 Spring 框架的任督二脉,事件和监听也是 Spring 框架必学的核心知识之一. 一般来说,我们很少会使用到应用程序事件,但我们也不 ...

  4. SpringBoot事件监听机制及观察者模式/发布订阅模式

    目录 本篇要点 什么是观察者模式? 发布订阅模式是什么? Spring事件监听机制概述 SpringBoot事件监听 定义注册事件 注解方式 @EventListener定义监听器 实现Applica ...

  5. Servlet 应用程序事件、监听器

    Web容器管理Servlet/JSP相关的生命周期,若对HttpServletRequest对象.HttpSession对象.ServletContxt对象在生成.销毁或相关属性设置发生的时机点有兴趣 ...

  6. Activiti6事件及监听器配置(学习笔记)

    1.事件及监听器原理 当流程引擎启动的时候,我们定义的监听器,就已经注册在一个事件类型上面. 注册的方式有多种,它可以注册在所有的事件类型上面.也可以注册在指定的几个事件类型上面,这样引擎启动的时候就 ...

  7. springboot使用HttpSessionListener 监听器统计当前在线人数

    概括: request.getSession(true):若存在会话则返回该会话,否则新建一个会话. request.getSession(false):若存在会话则返回该会话,否则返回NULL ht ...

  8. SpringBoot -- 事件(Application Event)

    Spring的事件为Bean与Bean之间的消息通信提供了支持,当一个Bean处理完一个任务之后,希望另外一个Bean知道并能做相应的处理,这时我们就需要让一个Bean监听当前Bean所发送的事件. ...

  9. Spring的事件和监听器

    Application下抽象子类ApplicationContextEvent的下面有4个已经实现好的事件 ContextClosedEvent(容器关闭时) ContextRefreshedEven ...

  10. Android的按钮单击事件及监听器四种常见的实现方式

    第一种:匿名内部类作为事件监听器类<ignore_js_op>大部分时候,事件处理器都没有什么利用价值(可利用代码通常都被抽象成了业务逻辑方法),因此大部分事件监听器只是临时使用一次,所以 ...

随机推荐

  1. FLink17--全窗口聚合方法1--ApplyWindowApp

    一.依赖 二.代码 package net.xdclass.class11; import java.util.List; import java.util.stream.Collectors; im ...

  2. Luogu P11543 Code+#5 我有矩阵,你有吗? 题解 [ 绿 ] [ 扩展域并查集 ]

    我有矩阵,你有吗?:并查集小清新题. 思路 看到这题,我第一个想到的竟然是高斯消元. 首先一行和一列肯定不会操作两次以上,不然一定可以等效为操作 \(0\) 次和操作 \(1\) 次的情况. 于是我们 ...

  3. 重磅发布!DeepSeek 微调秘籍揭秘,一键解锁升级版全家桶,AI 玩家必备神器!

    DeepSeek V3/R1 火爆全网,基于原始模型的解决方案和 API 服务已随处可见,陷入低价和免费内卷. 如何站在巨人肩膀上,通过后训练(post-training)结合专业领域数据,低成本打造 ...

  4. 多项式算法初探:从 FFT 到 NTT

    注:由于发现 FWT 解决的问题和 FFT,NTT 差别有点大,加之 FMT 的存在,本文就只解决 FFT 和 NTT,剩下两个放在别的算法总结里讲. 多项式一向是算法竞赛中相当博大精深的东西,作为一 ...

  5. UpdateHub-一款好看且免费开源的Windows软件更新检测工具

    UpdateHub 是一款简化计算机上软件更新的应用程序.用户友好的界面允许您快速检查和安装操作系统和应用程序的可用更新. 通过这个应用,你可以快速地查看设备上安装的所有软件的更新,包括操作系统和应用 ...

  6. CMD批处理脚本+VBScript脚本+Potplayer 实现文件夹内所有视频的截图任务(指定时间点)

    实现自动化视频截图,一般会直接借视频编解码如FFmpeg,动用相关函数来实现,直接从解码源头设计程序.然而我没有接触过FFmpeg,借助cmd批处理,以及vbs,还有现成的播放器potplayer,一 ...

  7. PHP将变量存储在数据库中,读取并执行变量的方法

    http://www.edbiji.com/doccenter/showdoc/4/nav/1214.html 例如将下边的字符串存储到数据库中您好,您的验证码是".$authcode.&q ...

  8. HTTP请求中包含账号密码

    如果你需要在HTTP请求中包含账号密码,你可以使用基本的HTTP身份验证.在C#中,你可以通过设置 HttpClient 的 DefaultRequestHeaders 来添加身份验证信息.以下是修改 ...

  9. C# 之委托的多播

    1 delegate void NumberCalculator(int a); 2 class Program 3 { 4 static int num1 = 100; 5 static void ...

  10. Dify 和 Manus 的技术架构差异

    Dify 框架能够部分实现 Manus 的功能效果,但在复杂任务自动化.多代理协作等领域存在技术差距. 一.核心功能对比 1. 任务拆解与执行能力 Dify:支持通过 Agent 模式 进行任务分解, ...