当前位置: 首页 > article >正文

spring的事件驱动有时候比消息队列好用

欢迎来到我的博客,代码的世界里,每一行都是一个故事


在这里插入图片描述

🎏:你只管努力,剩下的交给时间

🏠 :小破站

spring的事件驱动有时候比消息队列好用

    • 前言
    • 什么是spring的事件驱动
      • 事件驱动模型简介
      • Spring 中的事件驱动机制
      • 实现步骤
        • 1. 定义事件类
        • 2. 实现监听器
        • 3. 发布事件
      • 典型使用场景
      • 优点
    • Spring Boot 事件驱动的优点分析
      • 1. 解耦
      • 2. 开箱即用
      • 3. 异步支持
      • 4. 更低成本
    • 与消息队列的对比
      • 适用场景
      • 性能对比
      • 复杂度
      • 扩展性
    • 实战
      • 定义事件
      • 定义监听
      • 定义发送者

前言

在现代开发中,异步处理越来越受到青睐。常见方案是通过消息队列实现异步通信,但Spring Boot的事件驱动模型提供了一种更轻量级的选择。本文将带你了解事件驱动的基本原理及其应用场景。

什么是spring的事件驱动

Spring Boot 的 事件驱动 是一种实现应用程序内组件之间松耦合通信的机制,常用于异步处理。它通过事件发布者和事件监听者的方式,将不同模块的逻辑分离,增强代码的可维护性和可扩展性。


事件驱动模型简介

事件驱动模型是一种以事件为中心的设计模式,其核心是通过发布者(Publisher)和订阅者(Subscriber)的解耦实现异步处理。主要特点包括:

  • 松耦合:事件发布者无需知道谁会处理事件,订阅者无需了解事件来自哪里。
  • 异步性:事件触发后,处理逻辑可以同步或异步执行。
  • 扩展性强:可以轻松添加新的事件类型或订阅者。

Spring 中的事件驱动机制

Spring 提供了一个内置的事件驱动机制,基于以下核心组件:

  1. 事件(Event):事件类需要继承 ApplicationEvent,是事件的载体,包含事件相关数据。

  2. 监听器(Listener)

    • 通过实现 ApplicationListener 接口处理事件。
    • 或者使用 @EventListener 注解定义事件处理方法。
  3. 事件发布器(Publisher)

    • 通过 ApplicationEventPublisher 将事件广播给所有合适的监听器。

实现步骤

1. 定义事件类

事件类需要继承 ApplicationEvent,并包含需要传递的业务数据。例如:

public class UserRegisteredEvent extends ApplicationEvent {
    private final String userEmail;

    public UserRegisteredEvent(Object source, String userEmail) {
        super(source);
        this.userEmail = userEmail;
    }

    public String getUserEmail() {
        return userEmail;
    }
}
2. 实现监听器

监听器可以通过两种方式实现:

  1. 实现 ApplicationListener 接口
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class UserRegisteredListener implements ApplicationListener<UserRegisteredEvent> {
    @Override
    public void onApplicationEvent(UserRegisteredEvent event) {
        System.out.println("User registered with email: " + event.getUserEmail());
    }
}
  1. 使用 @EventListener 注解
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class NotificationService {
    @EventListener
    public void handleUserRegisteredEvent(UserRegisteredEvent event) {
        System.out.println("Sending welcome email to: " + event.getUserEmail());
    }
}
3. 发布事件

在需要触发事件的地方使用 ApplicationEventPublisher 发布事件。例如:

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    private final ApplicationEventPublisher eventPublisher;

    public UserService(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    public void registerUser(String userEmail) {
        // 用户注册逻辑
        System.out.println("Registering user: " + userEmail);
        // 发布事件
        eventPublisher.publishEvent(new UserRegisteredEvent(this, userEmail));
    }
}

典型使用场景

  1. 用户注册后发送欢迎邮件
  2. 数据变更通知:比如订单状态更新通知。
  3. 模块之间解耦:将跨模块的逻辑通过事件分发处理。

优点

  • 松耦合:使代码更易维护和扩展。
  • 灵活性高:可以随时添加新功能,而无需修改现有逻辑。
  • 支持异步:可配合 @Async 注解,实现事件的异步处理。

通过 Spring 的事件驱动机制,可以显著提高应用的模块化程度和扩展性。结合实践,这种模式在处理复杂的业务逻辑时非常高效!

Spring Boot 事件驱动的优点分析

1. 解耦

  • 核心优势

    :发布者与订阅者之间不存在直接依赖关系,业务逻辑完全分离。

    实现方式

    :通过事件对象充当“消息载体”,发布者仅负责发布事件,监听者根据事件类型处理对应逻辑。

    好处

    • 更清晰的代码结构。
    • 易于添加新功能:无需修改现有的发布者逻辑,只需新增监听器。
    • 提高代码的可维护性和可测试性。

2. 开箱即用

  • 无需额外依赖中间件:Spring 提供了内置的事件驱动机制,直接通过 ApplicationEventPublisher@EventListener 即可实现。
  • 简单易用:开发者无需学习或配置复杂的框架,即可快速上手,适合对性能和功能没有特殊要求的小型项目。

3. 异步支持

  • 结合 @Async 注解

    :Spring 的事件机制与异步能力无缝集成,通过在监听器方法上添加

    @Async
    

    ,即可将事件处理从主线程中分离出来:

    @EventListener
    @Async
    public void handleEvent(UserRegisteredEvent event) {
        // 异步处理逻辑
    }
    
  • 优点

    • 提高响应速度:主线程只负责事件发布,耗时逻辑可由监听器异步完成。
    • 充分利用多线程环境:提高系统性能和吞吐量。

4. 更低成本

  • 适合小型、内部异步处理需求

    • 对于无需跨服务或分布式架构的简单场景,使用 Spring 事件机制避免了引入消息队列(如 Kafka、RabbitMQ)带来的复杂性。

    • 典型场景

      • 用户注册后发送邮件或短信通知。
      • 数据变更后触发简单的后续处理逻辑(如缓存更新)。
    • 成本对比

      • 中间件通常涉及安装、配置、监控和运维,而 Spring 的事件驱动机制只需 Spring 框架本身即可运行,降低了实现和运维成本。

与消息队列的对比

适用场景

  • 事件驱动:适用于单体应用或小型系统,业务逻辑相对简单,性能和可靠性需求一般,如注册后发送邮件、更新日志等。
  • 消息队列:适用于分布式系统、高并发、高可靠性要求的场景,例如跨服务通信、任务排队处理、大量数据流转等。

性能对比

  • 事件驱动:无需网络通信,事件在应用内部直接传递,响应速度快,性能更高。
  • 消息队列:依赖网络传输,可能受到网络延迟影响,但在吞吐量和消息持久化方面表现更优。

复杂度

  • 事件驱动:实现简单,无需额外运维支持。Spring 提供的机制可以开箱即用,开发和部署成本低。
  • 消息队列:需要额外的运维工作,包括安装、配置、监控和优化。例如 Kafka、RabbitMQ 等需要专业知识和工具支持。

扩展性

  • 事件驱动:适合单体架构,随着业务复杂度增加,可能难以应对跨服务的需求。
  • 消息队列:支持分布式架构和跨服务通信,能够处理复杂场景和更大规模的任务,且易于扩展。

实战

image-20250115155802246

定义事件

package fun.acowbo.event.event;

import org.springframework.context.ApplicationEvent;

/**
 * @author <a href="https://acowbo.fun">acowbo</a>
 * @since 2025/1/15
 */
public class DeviceCreateSuccessEvent extends ApplicationEvent {
    /**
     * description: 设备注册成功事件
     * @param source topic名称
     * @since 2025/1/15
     */
    public DeviceCreateSuccessEvent(String source) {
        super(source);
    }
}

定义监听

package fun.acowbo.event.monitor;

import fun.acowbo.event.event.DeviceCreateSuccessEvent;
import fun.acowbo.exception.BusinessException;
import fun.acowbo.kafka.KafkaTopicAdmin;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * @author <a href="https://acowbo.fun">acowbo</a>
 * @since 2025/1/15
 */
@Component
@Slf4j
public class DeviceCreateListener {

    @Resource
    private KafkaTopicAdmin kafkaTopicAdmin;
    @EventListener(DeviceCreateSuccessEvent.class)
    public void onDeviceCreate(DeviceCreateSuccessEvent event) {
        try {
            kafkaTopicAdmin.createTopic((String) event.getSource(), 6, (short) 3, null);
            log.info("创建topic成功");
        } catch (Exception e) {
            throw new BusinessException("创建topic失败");
        }
    }
}

定义发送者

@Resource
    private ApplicationContext applicationContext;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean saveDeviceManagement(DeviceManagementReqVO bean) {
        Boolean isAdd = bean.getIsAdd();
        DeviceManagement deviceManagement = BeanUtil.toBean(bean, DeviceManagement.class);
        if (isAdd) {
            deviceManagement.setId(IdUtil.getSnowflakeNextId());
            // deviceManagement.setCreateBy(StpUtil.getLoginIdAsLong());
            boolean save = mapper.insert(deviceManagement) > 0;
            if (save) {
                applicationContext.publishEvent(new DeviceCreateSuccessEvent(deviceManagement.getTopicName()));
            }
            return save;
        }
        return mapper.updateById(deviceManagement) > 0;
    }

http://www.kler.cn/a/536867.html

相关文章:

  • CSS(三)less一篇搞定
  • Deno vs Node.js:性能对比深度解析
  • Redis --- 秒杀优化方案(阻塞队列+基于Stream流的消息队列)
  • 算法:线性同余法(LCG,Linear Congruential Generator)
  • OpenAI 实战进阶教程 - 第六节: OpenAI 与爬虫集成实现任务自动化
  • Pytest+selenium UI自动化测试实战实例
  • 【Docker】 Manifest与Buildx:多架构镜像管理的解析与实践
  • 自己做了个微信小游戏:推一个箱子
  • 基于钉钉API的连接器实现:企业数据集成与自动化管理
  • 大模型产品Deepseek(五)、本地安装部署(Docker方式)
  • 【C语言】数 组与指针:深度剖析与等价表达
  • 力扣240 搜索二维矩阵 ll
  • golang命令大全13--相关资源与学习路径【完】
  • <论文>DeepSeek-R1:通过强化学习激励大语言模型的推理能力
  • python-leetcode-除法求值
  • UML学习
  • 【dotnet】安全编码规范
  • 【清晰教程】通过Docker为本地DeepSeek-r1部署WebUI界面
  • 2025年2月2日(多任务 线程)
  • vue3 的 onScopeDispose 是什么作用
  • 【数据结构-C语言】绪论
  • 0207算法:寻找目标值、库存管理
  • 101.对称二叉树 python
  • 【现代深度学习技术】深度学习计算 | 读写文件
  • UdpServer
  • springboot基于微信小程序的仓储管理系统