Java Spring 事件驱动机制
文章目录
- Java Spring 事件驱动机制详解:ApplicationEventPublisher 与 ApplicationEventPublisherAware
- 一、什么是 Spring 的事件机制?
- 二、`ApplicationEventPublisher` 接口
- 示例:使用 `ApplicationEventPublisher` 发布事件
- 三、`ApplicationEventPublisherAware` 接口
- 四、自定义事件类
- 五、事件监听器的两种写法
- 1. 实现 `ApplicationListener` 接口
- 2. 使用 `@EventListener` 注解(推荐)
- 六、实战演示
- 七、使用场景总结
- 八、使用场景
- 九、直接调用、spring事件机制、MQ
- 1. **直接调用**
- 2. **Spring事件机制**
- 3. **MQ 消息队列方式(推荐用于服务间通信)**
- ✅ 结论建议
- 🔍 补充提示
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;@Overridepublic 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> {@Overridepublic 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 {@EventListenerpublic 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 {@Resourceprivate EventPublisher publisher;@Testpublic 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、重试、死信队列 | 高 | 可做到“最终一致性” |