【事件驱动】两个独立模块如何实现解耦,除了消息队列还有没有其他方式?

前言

先说一下需求。如果用户注册了之后,想发送短信或者邮箱提醒注册成功。又因为用户跟发送信息是两个独立的模块,不想过多耦合。这时候该怎么做?

一般来说可以加一个消息中间件,比如 kafka,这也是最常见的一种方法,高性能。除了这个不加其他技术栈,就用 spring 解决的?

那就是事件驱动ApplicationEvent

正文

ApplicationEvent 是 Spring 框架中用于实现事件驱动编程的关键组件之一。让我详细解释一下:

什么是 ApplicationEvent

  • ApplicationEvent 是 Spring 框架中的一个类,用于表示应用程序中的事件
  • 事件可以是应用程序内部的状态变化、用户操作、外部系统通信等。
  • 通过发布和监听事件,我们可以实现松散耦合的组件之间的通信。

如何使用 ApplicationEvent

  • 创建自定义事件类:可以扩展 ApplicationEvent 类来创建自己的事件。例如,用户注册事件、消息发送事件等。
  • 发布事件:在适当的时机,可以使用 ApplicationEventPublisher 接口发布事件。
  • 监听事件:通过实现事件监听器,可以在事件发生时执行相应的操作。

示例:用户注册事件

  • 首先,我们创建一个自定义事件类 UserRegistrationEvent 继承自 ApplicationEvent

    java 复制代码
    public class UserRegistrationEvent extends ApplicationEvent {
    
        private String username;
    
        public UserRegistrationEvent(Object source, String username) {
            super(source);
            this.username = username;
        }
    
        public String getUsername() {
            return username;
        }
    }
  • 然后,我们创建一个服务类 UserService,用于处理用户注册逻辑并发布事件:

    java 复制代码
    @Service
    public class UserService {
        @Autowired
        private ApplicationEventPublisher eventPublisher;
    
        public void registerUser(String username) {
            // ... 用户注册逻辑 ...
    
            // 发布 UserRegistrationEvent 事件
            eventPublisher.publishEvent(new UserRegistrationEvent(this, username));
        }
    }
  • 再创建消息服务类 Message,用于处理消息发送

    java 复制代码
    @Service
    public class MessageService {
    
        private static Logger logger = LoggerFactory.getLogger(MessageService.class);
    
        public void sendMessage(String username) {
            logger.info("MessageService.sendMessage():{}",username);
        }
    
    }
  • 最后,我们创建一个事件监听器 UserRegistrationListener,以便在用户注册时执行相应的操作:

    java 复制代码
    @Component
    public class UserRegistrationListener {
    
        private Logger logger = LoggerFactory.getLogger(getClass());
    
        private ScheduledExecutorService executorService = null;
    
        @Autowired
        private MessageService messageService;
    
        /**
         * 事件处理监听
         * @param event
         */
        @EventListener
        public void handleUserRegistration(UserRegistrationEvent event) {
            logger.info("监听到一条新的信息:{}", event.getUsername());
    
            // 延迟一段时间后再创建
            if (executorService == null) {
                executorService = Executors.newScheduledThreadPool(1);
            }
            // 延迟一段时间后再执行,防止流程流转时数据还没更新到数据库
            long delayInSeconds = 1; // 延迟1秒
            executorService.schedule(() -> {
                // 执行事件
                messageService.sendMessage(event.getUsername());
            }, delayInSeconds, TimeUnit.SECONDS);
        }
    
        @PreDestroy
        public void destroy() {
            // 执行清理操作
            executorService.shutdown();
        }
    
    }
  • 测试

    java 复制代码
    @RestController
    @RequestMapping("/user/test")
    public class UserController {
    
        private static Logger logger = LoggerFactory.getLogger(UserController.class);
    
        @Autowired
        private UserService userService;
    
        @PostMapping
        public CommonResult userRegistration() {
            String username = "user1";
            logger.info("注册用户:{}" , username);
            userService.registerUser(username);
            return CommonResult.ok();
        }
    
    }

总结

  • 使用 ApplicationEvent,可以构建松散耦合且可扩展的应用程序。
  • 通过自定义事件、事件发布者和事件监听器,您可以实现事件驱动的编程模型,提高应用程序的灵活性和可维护性。
相关推荐
SoFlu软件机器人8 分钟前
高并发秒杀系统设计:关键技术解析与典型陷阱规避
java·人工智能
橘猫云计算机设计8 分钟前
基于django云平台的求职智能分析系统(源码+lw+部署文档+讲解),源码可白嫖!
数据库·spring boot·后端·python·django·毕业设计
码农小站11 分钟前
MyBatis-Plus 表字段策略详解:@TableField(updateStrategy) 的配置与使用指南
java·后端
技术小丁14 分钟前
使用PHP将PDF转换为图片(windows + PHP + ImageMagick)
后端
李憨憨17 分钟前
深入探究MapStruct:高效Java Bean映射工具的全方位解析
java·后端
仰望星空的打工人17 分钟前
若依改用EasyCaptcha验证码
后端
雷渊18 分钟前
通俗易懂的来解释倒排索引
java·后端·面试
知其然亦知其所以然18 分钟前
面试官狂喜!我用这 5 分钟讲清了 ThreadPoolExecutor 饱和策略,逆袭上岸
java·后端·面试
独立开阀者_FwtCoder21 分钟前
分享 8 个 丰富的 Nextjs 模板网站
前端·javascript·后端