【Springboot系列】解析Springboot事件机制,从入门到大师

news2024/11/15 21:39:52

系列文章:Spring Boot学习大纲,可以留言自己想了解的技术点

继续写Springboot系列,争取早点结束。

1、是什么

Spring的事件(Application Event)为Bean与Bean之间的消息通信提供了支持

事件机制中有三种角色:发布事件者、事件、事件监听者

发布事件者:发布事件的对象

事件:事件的具体内容

事件监听者:等待处理时间的对象

2、内置有哪些事件

先看下类图,看的很明白

图上可以看到ApplicationEvent有两个子类

ApplicationContextEvent 是Spring提供的事件监听,包路径为:org.springframework.context.event

SpringApplicationEvent 是Springboot的扩展事件,包路径为:org.springframework.boot.context.event

区别懂了吗

Spring提供5种标准的事件监听:

  1. 上下文更新事件(ContextRefreshedEvent):该事件会在ApplicationContext被初始化或者更新时发布。也可以在调用ConfigurableApplicationContext接口中的refresh()方法时被触发。
  2. 上下文开始事件(ContextStartedEvent):当容器ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。
  3. 上下文停止事件(ContextStoppedEvent):当容ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。
  4. 上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。
  5. 请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结束触发该事件。

Spring Boot扩展了Spring的ApplicationContextEvent,提供了四种事件:

  1. ApplicationStartedEvent :spring boot启动开始时执行的事件
  2. ApplicationEnvironmentPreparedEvent:spring boot 对应Enviroment已经准备完毕,但此时上下文context还没有创建。
  3. ApplicationPreparedEvent:spring boot上下文context创建完成,但此时spring中的bean是没有完全加载完成的。
  4. ApplicationFailedEvent:spring boot启动异常时执行事件

3、自定义

内置的事件再多也无法满足自定义的需要,所以怎么实现自己的事件才是我们学习的目标,理解这套框架

3.1 新建一个Springboot web项目

一路next就好,勾选web,别犹豫

3.2 创建自定义事件,继承ApplicationEvent

这里只是加了一个name,在正常开发中,可以设置一些自定义的属性

public class CarawayEvent extends ApplicationEvent {
    private final String name;
    public CarawayEvent(Object source, String name) {
        super(source);
        this.name = name;
    }
    public String getName() {
        return name;
    }
}

3.3 创建监听器Listener,实现ApplicationListener

这里的泛型用你监听的事件类型

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class CarawayEventListener implements ApplicationListener<CarawayEvent> {
    @Override
    public void onApplicationEvent(CarawayEvent event) {
        System.out.println(event.getName());
    }
}

3.4 发布事件

这边写了一个接口,使用了ApplicationEventPublisher发布事件

@RestController
public class TestController {
    @Autowired
    private ApplicationEventPublisher publisher;
    @GetMapping("/")
    public String test(HttpServletRequest request){
        CarawayEvent event = new CarawayEvent(this, "香菜");
        publisher.publishEvent(event);
        return "hello";
    }
}

4、看源码

看源码的方式很简单,把上面大代码写好,一步一步debug就好了

4.1 注册        

直接进到方法 org.springframework.context.support.AbstractApplicationContext#registerListeners

这里只能注册实现了ApplicationListener的bean

 

注解方式的通过 org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization

最终进入到org.springframework.context.event.EventListenerMethodProcessor#processBean

 

4.2 ApplicationEventMulticaster

ApplicationEventMulticaster是spring中事件广播器接口,负责事件的广播发布。

AbstractApplicationContext中使用initApplicationEventMulticaster()初始化事件广播器。

  • 如果容器里面有名为applicationEventMulticaster的bean,这将该bean设为上下文中的事件广播器。
  • 如果容器里面没有applicationEventMulticaster的bean,默认创建SimpleApplicationEventMulticaster来代替
/**
 * Initialize the ApplicationEventMulticaster.
 * Uses SimpleApplicationEventMulticaster if none defined in the context.
 * @see org.springframework.context.event.SimpleApplicationEventMulticaster
 */
protected void initApplicationEventMulticaster() {
   ConfigurableListableBeanFactory beanFactory = getBeanFactory();
   if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
      this.applicationEventMulticaster =
            beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
      if (logger.isTraceEnabled()) {
         logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
      }
   }
   else {
      this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
      beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
      if (logger.isTraceEnabled()) {
         logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
               "[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
      }
   }
}

4.3 触发流程

最终在org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent 触发

 

5 杂项

5.1 Spring Boot事件监听有四种方式

  • 手工向ApplicationContext中添加监听器
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
context.addApplicationListener(new CarawayListener());//这里注册事件监听器
  • 在application.properties中配置监听器

context.listener.classes=com.com.xin.event.CarawayEventListener

  • 使用@Component 注解

就是上面3.3自定义的方式

  • 通过@EventListener注解实现事件监听
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class CarawayAnnotationListener {
    @EventListener
    public void listener1(CarawayEvent event) {
        System.out.println("listener1  -- " + event.getName());
    }

    @EventListener
    public void listener2(CarawayEvent event) {
        System.out.println("listener2  -- " + event.getName());
    }
}

5.2 自定义线程池

在翻代码的时候发现事件有一个属性是taskExecutor,这个一看就是线程池,

如果不配置线程池所有的事件在主线程中执行。

自己配一个

@Configuration
public class ThreadConfig {
    //BeanName 必须是这个
    @Bean("applicationEventMulticaster")
    public SimpleApplicationEventMulticaster simpleApplicationEventMulticaster(BeanFactory beanFactory, ThreadPoolTaskExecutor carawayExecutor){
        SimpleApplicationEventMulticaster simpleApplicationEventMulticaster
                = new SimpleApplicationEventMulticaster(beanFactory);
        simpleApplicationEventMulticaster.setTaskExecutor(carawayExecutor);
        return simpleApplicationEventMulticaster;
    }
    @Bean("carawayExecutor")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(4);
        executor.setMaxPoolSize(4);
        executor.setThreadNamePrefix("caraway_task_executor_thread");
        executor.initialize();
        return executor;
    }
}

5.3 发布事件的方式

通过ApplicationEventPublisher 发布

通过ApplicationContext接口发布

@RestController
public class TestController {
    @Autowired
    private ApplicationEventPublisher publisher;
    @Autowired
    private ApplicationContext applicationContext;
    @GetMapping("/")
    public String test(HttpServletRequest request){
        CarawayEvent event = new CarawayEvent(this, "香菜");
        publisher.publishEvent(event);
        applicationContext.publishEvent(event);
        return "hello";
    }
}

6、总结

事件机制设计上还是很简单的,使用也很简单,

三个对象:事件源,发布者,监听者

事件的原理也很简单,但是使用的方式很多

推荐书

一本真正从漏洞靶场、项目案例来指导读者提高Web安全、漏洞利用技术与渗透测试技巧的图书。本书以新手实操为出发点,搭建漏洞靶场:解析攻防原理+详解攻防手法+构建完整攻防体系。

本书从一开始便对Web开发基础和靶场搭建做了详细介绍,结合红日安全团队的漏洞挖掘和评估项目实战经验对各种实战技术进行分析,便于读者理解书中讲到的项目评估攻防案例的底层逻辑

当当销售链接:《Web安全攻防从入门到精通 红日安全出品》(红日安全)【简介_书评_在线阅读】 - 当当图书

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/391168.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RTOS中事件集的实现原理以及实用应用

事件集的原理 RTOS中事件集的实现原理是通过位掩码来实现的。事件集是一种用于在任务之间传递信号的机制。在RTOS中&#xff0c;事件集通常是一个32位的二进制位向量。每个位都代表一个特定的事件&#xff0c;例如信号、标志、定时器等。 当一个任务等待一个或多个事件时&…

Hbase备份与恢复工具Snapshot的基本概念与工作原理

数据库都有相对完善的备份与恢复功能。备份与恢复功能是数据库在数据意外丢失、损坏下的最后一根救命稻草。数据库定期备份、定期演练恢复是当下很多重要业务都在慢慢接受的最佳实践&#xff0c;也是数据库管理者推荐的一种管理规范。HBase数据库最核心的备份与恢复工具——Sna…

Spark+Vue+Springboot 协同过滤额音乐推荐大数据深度学习项目

一、项目背景 随着互联网的发展,大数据的到来,传统的音乐行业受到了很大的冲击,原有的音乐数字化给人们生活带来了极大的便利。随着数字音乐的兴起,各大音乐平台层出不穷,人们在音乐平台上收听音乐的时,常常因为歌曲信息繁杂,而不能找到自己想听的音乐。为了解决这个问题,音乐…

Elasticsearch:使用 Logstash 构建从 Kafka 到 Elasticsearch 的管道 - Nodejs

在我之前的文章 “Elastic&#xff1a;使用 Kafka 部署 Elastic Stack”&#xff0c;我构建了从 Beats > Kafka > Logstash > Elasticsearch 的管道。在今天的文章中&#xff0c;我将描述从 Nodejs > Kafka > Logstash > Elasticsearch 这样的一个数据流。在…

modbus转profinet网关连接ABB变频器在博图程序案例

在博图里PLC无需编程利用兴达易控modbus转Profinet网关,将ABB变频器接入到西门子网络中.用到设备为西门子1200PLC&#xff0c;ABB变频器及兴达易控Modbus转profinet网关一个;兴达易控Modbus转profinet协议转换器&#xff08;XD-MDPN100&#xff09;一台 打开博图添加1200PLC&am…

121.(leaflet篇)leaflet结合echarts4迁徙图

听老人家说:多看美女会长寿 地图之家总目录(订阅之前建议先查看该博客) 文章末尾处提供保证可运行完整代码包,运行如有问题,可“私信”博主。 效果如下所示: 下面献上完整代码,代码重要位置会做相应解释 <!DOCTYPE html> <html>

【数据挖掘与商务智能决策】第二章 特征工程与数据预处理

数据预处理 非数值类型数据处理 Get_dummies哑变量处理 1. 简单示例&#xff1a;“男”和“女”的数值转换 import pandas as pd df pd.DataFrame({客户编号: [1, 2, 3], 性别: [男, 女, 男]}) df客户编号性别01男12女23男 df pd.get_dummies(df, columns[性别]) df客户…

DetectGPT:使用概率曲率的零样本机器生成文本检测

DetectGPT的目的是确定一段文本是否由特定的llm生成&#xff0c;例如GPT-3。为了对段落 x 进行分类&#xff0c;DetectGPT 首先使用通用的预训练模型&#xff08;例如 T5&#xff09;对段落 ~xi 生成较小的扰动。然后DetectGPT将原始样本x的对数概率与每个扰动样本~xi进行比较。…

浏览器主页被hao123劫持的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。喜欢通过博客创作的方式对所学的知识进行总结与归纳,不仅形成深入且独到的理…

Redis哨兵(Sentinel)模式

前言 上一期实现了Redis的主从复制架构&#xff0c;由于主从模式在主节点宕机故障时整个Redis服务都不能再执行写操作&#xff0c;而无法保证Redis在整个系统中的高可用。 Redis提供了Sentinel哨兵机制来解决以上问题&#xff0c;当哨兵服务监测到master下线或宕机&#xff0…

汽车标定知识整理(二):CCP报文基本命令介绍

目录 一、基本命令 CRO命令报文的基本命令表&#xff1a; 二、基本命令与可选命令帧格式介绍 1、CONNECT——建立连接&#xff08;0x01&#xff09; 2、GET_CPP_VERSION——获取CCP版本&#xff08;0x1B&#xff09; 3、SET_MTA——设置内存传输地址&#xff08;0x02&#…

FPGA_边沿监测理解

一、简易频率计设计中为什么一定要获取下降沿?gate_a:实际闸门信号gate_a_stand:将实际闸门信号打一拍之后的信号gate_a_fall_s:下降沿标志信号cnt_clk_stand: Y值&#xff0c;即在实际闸门信号下&#xff0c;标准时钟信号的周期个数cnt_clk_stand_reg:保存Y值的寄存器核心问题…

展示企业情况的BI数据可视化大屏怎么做?

做综合展示企业情况的BI数据可视化大屏&#xff0c;就意味着要综合展示多个子公司或者部门的数据情况。首先要解决的就是多系统数据的整合、数据孤岛的束缚&#xff0c;其次才是数据分析模型构建、BI数据可视化大屏报表的制作。 1、整合多系统数据&#xff0c;消除数据孤岛现象…

flink大数据处理流式计算详解

flink大数据处理 文章目录flink大数据处理二、WebUI可视化界面&#xff08;测试用&#xff09;三、Flink部署3.1 JobManager3.2 TaskManager3.3 并行度的调整配置3.4 区分 TaskSolt和parallelism并行度配置四、Source Operator(资源算子)五、Sink Operator(输出算子)六、Flink滑…

系统检测维护工具Wsycheck使用(18)

实验目的 &#xff08;1&#xff09;学习Wsycheck的基本功能&#xff1b; &#xff08;2&#xff09;掌握Wsycheck的基本使用方法&#xff1b; 预备知识 windows操作系统的基本知识如&#xff1a;进程、网络、服务和文件等的了解。 Wsycheck是一款强大的系统检测维护工具,进程和…

js求解《初级算法》19.删除链表的倒数第N个结点

一、题目描述 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5] 二、思路 用虚拟头结点&#xff0b;双指针的方法解决该题&#xff0c;我们知道题目要求我们返回的是…

SpringMVC源码:参数解析、方法调用与返回值处理

参考资料&#xff1a; 《SpringMVC源码解析系列》 《SpringMVC源码分析》 《Spring MVC源码》 写在开头&#xff1a;本文为个人学习笔记&#xff0c;内容比较随意&#xff0c;夹杂个人理解&#xff0c;如有错误&#xff0c;欢迎指正。 前文&#xff1a; 《SpringMVC源码&a…

并发包中的ConcurrentLinkedQueue和LinkedBlockingQueue有什么区别?

第20讲 | 并发包中的ConcurrentLinkedQueue和LinkedBlockingQueue有什么区别&#xff1f; 在上一讲中&#xff0c;我分析了 Java 并发包中的部分内容&#xff0c;今天我来介绍一下线程安全队列。Java 标准库提供了非常多的线程安全队列&#xff0c;很容易混淆。 今天我要问你的…

谷歌浏览器和火狐浏览器永久禁用缓存【一劳永逸的解决方式】

目录 前言 谷歌浏览器 方式一 方式二 火狐浏览器 前言 缓存对于开发人员来说异常的痛苦,很多莫名其妙的bug就是由缓存导致的,但当我们在网上查找禁用缓存的方式时,找到的方式大多数都是在开发者工具的面板中勾选禁用缓存的选项,但这种方式有个弊端就是需要一直打开这个…

软件测试工程师没有碰到算我输-2023最全的接口测试面试题及参考答案

接口测试最近几年被炒的火热了&#xff0c;越来越多的测试同行意识到接口测试的重要性。接口测试为什么会如此重要呢&#xff1f; 主要是平常的功能点点点&#xff0c;大家水平都一样&#xff0c;是个人都能点&#xff0c;面试时候如果问你平常在公司怎么测试的&#xff0c;你除…