Java Spring 事件驱动机制

发布于:2025-05-15 ⋅ 阅读:(13) ⋅ 点赞:(0)


Java Spring 事件驱动机制详解:ApplicationEventPublisher 与 ApplicationEventPublisherAware

一、什么是 Spring 的事件机制?

Spring 的事件机制是一种发布-订阅(Publish-Subscribe)模式的实现。它允许 Spring 容器中的 Bean 之间进行异步/同步通信,适合实现模块解耦、通知机制等场景。

事件驱动模型主要由三部分组成:

  • 事件(Event):继承自 ApplicationEvent 的自定义事件类
  • 事件发布者(Publisher):使用 ApplicationEventPublisher 发布事件
  • 事件监听器(Listener):通过实现 ApplicationListener 或使用 @EventListener 注解来接收事件

二、ApplicationEventPublisher 接口

ApplicationEventPublisher 是 Spring 的事件发布接口,提供如下方法:

void publishEvent(Object event);

当调用此方法时,Spring 会将事件广播给所有监听该类型事件的监听器。

示例:使用 ApplicationEventPublisher 发布事件

通过publishEvent方法,发布一个自定义事件类。MyCustomEvent就是自定义事件类

import com.wzw.entity.UserEvent;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class EventPublisher {

    private final ApplicationEventPublisher publisher;

    public void publish(String message) {
        publisher.publishEvent(new UserEvent(this, message));
    }
}

三、ApplicationEventPublisherAware 接口

如果不想使用构造函数或字段注入的方式获取 ApplicationEventPublisher,可以实现 ApplicationEventPublisherAware 接口,Spring 会自动注入该依赖:

import com.wzw.entity.UserEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;

@Component
public class EventPublisher implements ApplicationEventPublisherAware {

    private ApplicationEventPublisher publisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.publisher = applicationEventPublisher;
    }

    public void publish(String message) {
        publisher.publishEvent(new UserEvent(this, message));
    }
}

四、自定义事件类

可以创建一个继承自 ApplicationEvent 的事件类,也可以直接使用 POJO:

import org.springframework.context.ApplicationEvent;

public class UserEvent extends ApplicationEvent {

    private String name;

    public UserEvent(Object source, String name) {
        super(source);
        this.name = name;
    }

    public String getName() {
        return name;
    }

}

五、事件监听器的两种写法

1. 实现 ApplicationListener 接口

import com.wzw.entity.UserEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class EventListener implements ApplicationListener<UserEvent> {

    @Override
    public void onApplicationEvent(UserEvent event) {
        System.out.println("-----实现接口ApplicationListener的监听方法:"+event.getName());
    }
}

2. 使用 @EventListener 注解(推荐)

import com.wzw.entity.UserEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class AnnoEventListener {

    @EventListener
    public void handleEvent(UserEvent event) {
        System.out.println("注解监听器收到事件:" + event.getName());
    }
}

六、实战演示

测试方法

import com.wzw.Main;
import com.wzw.service.EventPublisher;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest(classes = Main.class)
public class PublisherTest {

    @Resource
    private EventPublisher publisher;

    @Test
    public void test() {
        publisher.publish("张三");
    }
}

在这里插入图片描述

七、使用场景总结

使用场景 推荐方式
解耦不同模块的业务逻辑 使用事件发布机制
想要自动注入发布器 使用构造函数注入 ApplicationEventPublisher
需要获取发布器但不想注入 实现 ApplicationEventPublisherAware
接收事件通知 实现监听器或使用 @EventListener

八、使用场景

Spring 的事件驱动机制是一个优雅的模块解耦方案。可以看做一个简单的MQ,常见用于:

  • 用户注册完成后发送欢迎邮件
  • 操作日志记录
  • 第三方系统通知(如消息推送、钉钉机器人)
  • 领域事件驱动设计(DDD)

九、直接调用、spring事件机制、MQ

特性 直接方法调用 Spring事件机制 消息队列(MQ)
解耦性 ❌ 紧耦合(调用者必须知道被调用者) ✅ 逻辑解耦(发布-监听) ✅ 系统解耦(服务间完全独立)
通信范围 进程内(方法内部) 应用内(单个 Spring 容器) 跨服务/跨系统
异步支持 ❌ 默认同步 ✅ 支持(结合 @Async ✅ 天生支持异步
容错能力 ❌ 方法出错调用方也失败 ✅ 可控制监听异常行为 ✅ 高容错(重试/积压/确认机制)
消息持久化 ❌ 无 ❌ 无 ✅ 有(如 RabbitMQ、Kafka)
性能 ✅ 极快(本地内存) ⚠️ 中等(有事件分发开销) ⚠️ 较慢(涉及网络、序列化)
运维成本 ✅ 零 ✅ 极低 ❌ 高(部署、监控、保障消息可靠)
应用场景 模块间简单调用 应用内模块解耦、观察者模式 服务间异步通信、削峰、广播、事务最终一致性
顺序保障 ✅ 同步调用按顺序执行 ⚠️ 依赖监听器线程实现 ⚠️ 依赖 MQ 实现(如 Kafka 分区)

✅ 举例对比:用户注册场景

场景:用户注册成功后,需完成以下动作:

  • 发送欢迎邮件
  • 写入操作日志
  • 向 CRM 系统推送用户数据

1. 直接调用

userService.register(user);
mailService.sendWelcome(user);
logService.writeLog(user);
crmService.pushUser(user);

✅ 简单
❌ 强耦合,crmService 出错可能影响主流程


2. Spring事件机制

userService.register(user);
eventPublisher.publishEvent(new UserRegisteredEvent(user));

然后由多个监听器来处理:

@EventListener
public void sendWelcomeEmail(UserRegisteredEvent event) { ... }

@EventListener
public void writeLog(UserRegisteredEvent event) { ... }

@EventListener
public void pushToCRM(UserRegisteredEvent event) { ... }

✅ 业务解耦
⚠️ 仍在同一个进程内,CRM 如果慢,仍可能拖慢主线程(除非加 @Async


3. MQ 消息队列方式(推荐用于服务间通信)

userService.register(user);
mqProducer.send("user.registered", user);
  • 邮件服务、日志服务、CRM 服务分别订阅 user.registered 主题并异步处理
  • 可持久化、失败重试、异步处理

✅ 系统间彻底解耦
✅ 网络分布式通信
⚠️ 成本高,需要部署 MQ 中间件(如 Kafka、RabbitMQ、RocketMQ)


✅ 结论建议

使用场景 建议方案
简单的同步调用 直接调用
应用内的解耦、逻辑分发(中小项目) Spring 事件机制
微服务之间、异步通信、可靠投递需求 消息队列(Kafka、RabbitMQ 等)

🔍 补充提示

使用方式 可靠性保障 扩展性 异常处理
直接调用 全依赖代码 主方法失败,整体失败
Spring事件 局部 try-catch 或 @Async + @TransactionalEventListener 某监听失败不影响主流程(可控制)
MQ 支持 ACK、重试、死信队列 可做到“最终一致性”


网站公告

今日签到

点亮在社区的每一天
去签到