Spring 事件监听机制源码

简介: Spring 提供了事件发布订阅机制,广泛应用于项目中。本文介绍了如何通过自定义事件类、订阅类和发布类实现这一机制,并展示了如何监听 SpringBoot 启动过程中的多个事件(如 `ApplicationStartingEvent`、`ApplicationEnvironmentPreparedEvent` 等)。通过掌握这些事件,可以更好地理解 SpringBoot 的启动流程。示例代码展示了从事件发布到接收的完整过程。

Spring 事件发布订阅机制

Spring 提供了许多非常好用的机制,比如IOC,AOP。这些几乎在所有的Spring项目中都有广泛的使用,这里讲解的是Spring提供的事件发布订阅机制,掌握发布订阅设计模式可以更好的在项目中对功能进行设计,也多一种解决方案。同时如果你掌握了SpringBoot的事件发布的全部流程,你就掌握了SpringBoot在整个启动过程中干了什么事,走了哪些流程

使用案例

事件类

scala

代码解读

复制代码

public class MyEvent extends ApplicationEvent {
    public MyEvent(Object source) {
        super(source);
    }
}

订阅类

typescript

代码解读

复制代码

@Component
public class MyEventSubscribe implements ApplicationListener<MyEvent> {
    @Override
    public void onApplicationEvent(MyEvent event) {
        String msg = (String) event.getSource();
        System.out.println("我接受到了事件,msg: "+msg);
    }
}

发布类

java

代码解读

复制代码

@Component
public class MyTest implements CommandLineRunner {
    @Autowired
    ApplicationEventPublisher applicationEventPublisher;

    @Override
    public void run(String... args) throws Exception {
        applicationEventPublisher.publishEvent(new MyEvent("hello event"));

    }
}

Log输出

vbnet

代码解读

复制代码

2024-09-25 14:44:23.852  INFO 22492 --- [           main] c.c.s.SpringResCode1Application          : Starting SpringResCode1Application using Java 1.8.0_202 on TCN1214966 with PID 22492 (D:\code\java\springResCode1\target\classes started by changtao.deng in D:\code\java\springResCode1)
2024-09-25 14:44:23.858  INFO 22492 --- [           main] c.c.s.SpringResCode1Application          : No active profile set, falling back to 1 default profile: "default"
我接受到了事件,msg: hello event
2024-09-25 14:44:24.555  INFO 22492 --- [           main] c.c.s.SpringResCode1Application          : Started SpringResCode1Application in 1.243 seconds (JVM running for 2.426)

除了自定义事件以外,你还可以监听SpringBoot 应用在启动过程中的发布的事件

typescript

代码解读

复制代码

@Component
public class MyEventSubscribe {

    @EventListener
    public void handleMyEvent1(ApplicationStartingEvent event) {
        Object source = event.getSource();
        System.out.println("我接受到了ApplicationStartingEvent事件,msg: " + source);
    }

    @EventListener
    public void handleMyEvent2(ApplicationEnvironmentPreparedEvent event) {
        Object source = event.getSource();
        System.out.println("我接受到了ApplicationEnvironmentPreparedEvent事件,msg: " + source);
    }

    @EventListener
    public void handleMyEvent3(ApplicationContextInitializedEvent event) {
        Object source = event.getSource();
        System.out.println("我接受到了ApplicationContextInitializedEvent事件,msg: " + source);
    }

    @EventListener
    public void handleMyEvent4(ApplicationPreparedEvent event) {
        Object source = event.getSource();
        System.out.println("我接受到了ApplicationPreparedEvent事件,msg: " + source);
    }

    @EventListener
    public void handleMyEvent6(ContextRefreshedEvent event) {
        Object source = event.getSource();
        System.out.println("我接受到了ContextRefreshed事件,msg: " + source);
    }
    
    @EventListener
    public void handleMyEvent5(ApplicationStartedEvent event) {
        Object source = event.getSource();
        System.out.println("我接受到了ApplicationStartedEvent事件,msg: " + source);
    }

    @EventListener
    public void handleMyEvent7(ContextClosedEvent event) {
        Object source = event.getSource();
        System.out.println("我接受到了ContextClosed事件,msg: " + source);
    }

    @EventListener
    public void handleMyEvent8(ContextStoppedEvent event) {
        Object source = event.getSource();
        System.out.println("我接受到了ContextStopped事件,msg: " + source);
    }
}

我这里还不是完全列举就写了八个事件,还是比较多的,那让我们执行下

vbnet

代码解读

复制代码

2024-09-25 15:20:39.298  INFO 12724 --- [           main] c.c.s.SpringResCode1Application          : No active profile set, falling back to 1 default profile: "default"
我接受到了ContextRefreshed事件,msg: org.springframework.context.annotation.AnnotationConfigApplicationContext@14cd1699, started on Wed Sep 25 15:20:39 CST 2024
2024-09-25 15:20:40.093  INFO 12724 --- [           main] c.c.s.SpringResCode1Application          : Started SpringResCode1Application in 1.272 seconds (JVM running for 2.278)
我接受到了ApplicationStartedEvent事件,msg: org.springframework.boot.SpringApplication@5f0e9815
我接受到了ContextClosed事件,msg: org.springframework.context.annotation.AnnotationConfigApplicationContext@14cd1699, started on Wed Sep 25 15:20:39 CST 2024

Process finished with exit code 0

并没有全部触发,只触发了ContextRefreshed,ApplicationStarted 以及最后的ContextClosed。

这里就先简单讲下这些事件

  1. ApplicationStartingEvent
  • 在运行开始时发送,但在任何处理开始之前。此时,监听器和初始化器还未被注册。
  1. ApplicationEnvironmentPreparedEvent
  • 在环境准备好后发送,但在创建 ApplicationContext 之前。这时,Environment 已经准备好,可以用于配置和处理。
  1. ApplicationContextInitializedEvent
  • ApplicationContext 初始化完成后发送,但在刷新之前。此时,所有的 ApplicationContextInitializer 已经被调用。
  1. ApplicationPreparedEvent
  • ApplicationContext 准备完成后发送,但在刷新之前。此时,所有的 Bean 定义已经加载,但尚未实例化。
  1. ApplicationStartedEvent
  • ApplicationContext 刷新并启动完成后发送。这标志着应用程序已经完全启动并准备好处理请求。
  1. ContextRefreshedEvent
  • ApplicationContext 完成刷新时发送。此时,所有的单例 Bean 已经被实例化并且已完成初始化。
  1. ContextStoppedEvent
  • ApplicationContext 停止时发送。此事件需要显式调用 stop() 方法。
  1. ContextClosedEvent
  • ApplicationContext 关闭时发送。这通常在 JVM 关闭或显式调用 close() 方法时发生。

因为我们的Bean是通过@Component注解来进行IOC注入的,所以上下文没有完成所有的Bean注入前的事件这个监听器是监听不到的,也就是ApplicationStartedEvent 之前的事件无法监听到。那有没有办法监听更前面的事件呢,其实也有,那就是通过SPI的方式进行注入,因为SPI的注入会在SpringContext的构造方法中就进行执行。

转载来源:https://juejin.cn/post/7418237666396946469

相关文章
|
23天前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
15天前
|
存储 关系型数据库 分布式数据库
GraphRAG:基于PolarDB+通义千问+LangChain的知识图谱+大模型最佳实践
本文介绍了如何使用PolarDB、通义千问和LangChain搭建GraphRAG系统,结合知识图谱和向量检索提升问答质量。通过实例展示了单独使用向量检索和图检索的局限性,并通过图+向量联合搜索增强了问答准确性。PolarDB支持AGE图引擎和pgvector插件,实现图数据和向量数据的统一存储与检索,提升了RAG系统的性能和效果。
|
20天前
|
机器学习/深度学习 算法 大数据
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
2024“华为杯”数学建模竞赛,对ABCDEF每个题进行详细的分析,涵盖风电场功率优化、WLAN网络吞吐量、磁性元件损耗建模、地理环境问题、高速公路应急车道启用和X射线脉冲星建模等多领域问题,解析了问题类型、专业和技能的需要。
2572 22
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
|
18天前
|
人工智能 IDE 程序员
期盼已久!通义灵码 AI 程序员开启邀测,全流程开发仅用几分钟
在云栖大会上,阿里云云原生应用平台负责人丁宇宣布,「通义灵码」完成全面升级,并正式发布 AI 程序员。
|
3天前
|
JSON 自然语言处理 数据管理
阿里云百炼产品月刊【2024年9月】
阿里云百炼产品月刊【2024年9月】,涵盖本月产品和功能发布、活动,应用实践等内容,帮助您快速了解阿里云百炼产品的最新动态。
阿里云百炼产品月刊【2024年9月】
|
2天前
|
存储 人工智能 搜索推荐
数据治理,是时候打破刻板印象了
瓴羊智能数据建设与治理产品Datapin全面升级,可演进扩展的数据架构体系为企业数据治理预留发展空间,推出敏捷版用以解决企业数据量不大但需构建数据的场景问题,基于大模型打造的DataAgent更是为企业用好数据资产提供了便利。
159 2
|
19天前
|
机器学习/深度学习 算法 数据可视化
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
2024年中国研究生数学建模竞赛C题聚焦磁性元件磁芯损耗建模。题目背景介绍了电能变换技术的发展与应用,强调磁性元件在功率变换器中的重要性。磁芯损耗受多种因素影响,现有模型难以精确预测。题目要求通过数据分析建立高精度磁芯损耗模型。具体任务包括励磁波形分类、修正斯坦麦茨方程、分析影响因素、构建预测模型及优化设计条件。涉及数据预处理、特征提取、机器学习及优化算法等技术。适合电气、材料、计算机等多个专业学生参与。
1571 16
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
|
21天前
|
编解码 JSON 自然语言处理
通义千问重磅开源Qwen2.5,性能超越Llama
击败Meta,阿里Qwen2.5再登全球开源大模型王座
950 14
|
3天前
|
Linux 虚拟化 开发者
一键将CentOs的yum源更换为国内阿里yum源
一键将CentOs的yum源更换为国内阿里yum源
190 2
|
17天前
|
人工智能 开发框架 Java
重磅发布!AI 驱动的 Java 开发框架:Spring AI Alibaba
随着生成式 AI 的快速发展,基于 AI 开发框架构建 AI 应用的诉求迅速增长,涌现出了包括 LangChain、LlamaIndex 等开发框架,但大部分框架只提供了 Python 语言的实现。但这些开发框架对于国内习惯了 Spring 开发范式的 Java 开发者而言,并非十分友好和丝滑。因此,我们基于 Spring AI 发布并快速演进 Spring AI Alibaba,通过提供一种方便的 API 抽象,帮助 Java 开发者简化 AI 应用的开发。同时,提供了完整的开源配套,包括可观测、网关、消息队列、配置中心等。
714 10