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

前言

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

一般来说可以加一个消息中间件,比如 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,可以构建松散耦合且可扩展的应用程序。
  • 通过自定义事件、事件发布者和事件监听器,您可以实现事件驱动的编程模型,提高应用程序的灵活性和可维护性。
相关推荐
程序员海军29 分钟前
用了两年多 GPT,我最近开始换模型了,还折腾了个求职避坑 Agent
前端·人工智能·后端
码士集团小青31 分钟前
SpringBoot 集成缓存性能之王 Caffeine
spring boot·后端·缓存
凤凰院凶涛QAQ2 小时前
《Java版数据结构 & 集合类剖析》Map & Set & 反射:“从哈希碰撞到红黑树转换,反射帮你扒开 HashMap 的桶数组看个究竟”
java·数据结构·算法
H.莓飛2 小时前
【数据结构】排序_OJ题(一):计数、桶与排序后的处理
java·开发语言·数据结构
wangchunyu1142 小时前
JAVA AQS与Lock实现原理(含CAS)
java·开发语言·word
excel2 小时前
IndexNow:让搜索引擎更快发现网站更新的主动通知协议
前端·后端·架构
Json____3 小时前
用 FastAPI + Vue3 打造一套完整的在线考试系统:从组卷到阅卷的业务闭环实践
后端·node.js·fastapi·it学习·wwwoop.com
w_zero_one4 小时前
算法--模拟
java·数据结构·算法
小灰灰搞电子4 小时前
Rust+Slint 实现FTP客户端调试助手源码分享
开发语言·后端·rust
Javatutouhouduan4 小时前
阿里1658页Java面试题总结(含答案)
java·高并发·java面试·java面试题·后端开发·java程序员·java八股文