Spring 的发布订阅模式(Publish-Subscribe Pattern)是一种基于事件驱动的设计模式,通过 "事件" 作为中间载体实现组件间的解耦。在这种模式中,"发布者"(Publisher)负责产生事件并发布,"订阅者"(Subscriber)通过订阅特定事件接收通知并处理,两者无需直接依赖,从而降低系统耦合度。
一、核心概念与角色
Spring 的发布订阅模式主要涉及三个核心角色:
事件(Event)
事件是发布者与订阅者之间的通信载体,封装了需要传递的数据。在 Spring 中,所有事件都需继承ApplicationEvent
(Spring 4.2 + 后可省略继承,直接使用普通类作为事件)。发布者(Publisher)
负责创建并发布事件的组件。Spring 中通过ApplicationEventPublisher
接口(或其实现类,如ApplicationContext
)来发布事件,调用publishEvent()
方法即可。订阅者(Subscriber)
负责监听并处理特定事件的组件。Spring 中订阅者可通过实现ApplicationListener
接口,或使用@EventListener
注解定义事件处理方法。
二、Spring 事件机制的核心组件
1. ApplicationEvent(事件基类)
ApplicationEvent
是 Spring 事件的基类,继承自 JDK 的EventObject
,包含事件源(source)和事件发生时间(timestamp)。
// Spring内置的ApplicationEvent
public abstract class ApplicationEvent extends EventObject {private final long timestamp; // 事件发生时间public ApplicationEvent(Object source) {super(source);this.timestamp = System.currentTimeMillis();}public final long getTimestamp() {return this.timestamp;}
}
自定义事件示例:
通常通过继承ApplicationEvent
定义业务事件:
// 自定义用户注册事件
public class UserRegisteredEvent extends ApplicationEvent {private User user; // 事件中携带的用户数据public UserRegisteredEvent(Object source, User user) {super(source);this.user = user;}public User getUser() {return user;}
}
Spring 4.2 + 后支持非继承 ApplicationEvent 的事件,直接使用普通类即可:
// 无需继承ApplicationEvent的事件
public class OrderCreatedEvent {private Order order;// 构造器、getter等
}
2. ApplicationListener(订阅者接口)
ApplicationListener
是订阅者的核心接口,用于定义事件处理逻辑,泛型参数指定需要监听的事件类型。
// Spring的事件监听器接口
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {// 事件处理方法,当监听的事件被发布时调用void onApplicationEvent(E event);
}
实现接口的订阅者示例:
// 监听UserRegisteredEvent的订阅者(发送欢迎邮件)
@Component
public class WelcomeEmailListener implements ApplicationListener<UserRegisteredEvent> {@Overridepublic void onApplicationEvent(UserRegisteredEvent event) {User user = event.getUser();System.out.println("给用户" + user.getName() + "发送欢迎邮件...");}
}
3. ApplicationEventPublisher(发布者接口)
ApplicationEventPublisher
是发布事件的接口,定义了发布事件的方法:
public interface ApplicationEventPublisher {// 发布事件void publishEvent(ApplicationEvent event);// Spring 4.2+新增,支持发布非ApplicationEvent类型的事件void publishEvent(Object event);
}
发布者的实现:
Spring 的ApplicationContext
(容器本身)实现了ApplicationEventPublisher
接口,因此可直接通过容器发布事件。实际开发中,通常通过依赖注入ApplicationEventPublisher
或ApplicationContext
来发布事件:
@Service
public class UserService {// 注入事件发布器@Autowiredprivate ApplicationEventPublisher publisher;public void register(User user) {// 1. 执行注册逻辑System.out.println("用户" + user.getName() + "注册成功");// 2. 发布用户注册事件publisher.publishEvent(new UserRegisteredEvent(this, user));}
}
三、注解驱动的事件监听(@EventListener)
Spring 4.2 引入@EventListener
注解,无需实现ApplicationListener
接口,直接在方法上标注即可定义事件处理逻辑,更简洁灵活。
基本用法
@Component
public class UserEventHandler {// 监听UserRegisteredEvent事件@EventListenerpublic void handleUserRegisteredEvent(UserRegisteredEvent event) {User user = event.getUser();System.out.println("处理用户注册事件:" + user.getName());}// 监听多个事件(方法参数为多个事件类型)@EventListenerpublic void handleMultiEvents(UserRegisteredEvent userEvent, OrderCreatedEvent orderEvent) {// 处理逻辑}
}
条件监听(condition)
通过condition
属性指定 SpEL 表达式,满足条件时才执行监听逻辑:
@EventListener(condition = "#event.user.age > 18") // 只处理成年用户的注册事件
public void handleAdultUserRegistered(UserRegisteredEvent event) {// 处理逻辑
}
事件顺序(@Order)
多个监听器监听同一事件时,通过@Order
指定执行顺序(值越小越先执行)
@Order(1) // 先执行
@EventListener
public void handleFirst(UserRegisteredEvent event) { ... }@Order(2) // 后执行
@EventListener
public void handleSecond(UserRegisteredEvent event) { ... }
四、异步事件处理
默认情况下,Spring 事件处理是同步的:发布者发布事件后,会等待所有监听器处理完成才继续执行。若需异步处理(不阻塞发布者),可通过以下步骤实现:
- 启用异步支持:在配置类上添加
@EnableAsync
注解。 - 标注异步方法:在监听方法上添加
@Async
注解。
示例:
// 1. 配置类启用异步
@Configuration
@EnableAsync
public class AsyncConfig {// 可选:自定义线程池@Beanpublic Executor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(5);executor.setMaxPoolSize(10);executor.setQueueCapacity(25);executor.initialize();return executor;}
}// 2. 异步处理事件的监听器
@Component
public class AsyncUserListener {@Async // 异步执行@EventListenerpublic void handleAsync(UserRegisteredEvent event) {System.out.println("异步处理事件:" + Thread.currentThread().getName());// 耗时操作(如发送短信、调用第三方接口等)}
}
五、事务绑定事件(@TransactionalEventListener)
在业务中,常需要在事务完成后(提交 / 回滚)再处理事件(例如:订单事务提交后再发送通知)。Spring 提供@TransactionalEventListener
注解,支持绑定事务生命周期。
注解的phase
属性指定事务阶段:
AFTER_COMMIT
:事务提交后(默认)AFTER_ROLLBACK
:事务回滚后AFTER_COMPLETION
:事务完成后(无论提交还是回滚)BEFORE_COMMIT
:事务提交前
示例:
@Service
public class OrderService {@Autowiredprivate ApplicationEventPublisher publisher;@Transactionalpublic void createOrder(Order order) {// 保存订单(事务内操作)orderRepository.save(order);// 发布事件(实际处理会在事务提交后)publisher.publishEvent(new OrderCreatedEvent(order));}
}@Component
public class OrderEventListener {// 订单事务提交后才处理事件@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)public void handleOrderCreated(OrderCreatedEvent event) {System.out.println("订单" + event.getOrder().getId() + "已提交,发送通知...");}
}
六、事件传播机制
Spring 事件具有层次性:监听器可监听父类事件,从而接收所有子类事件。例如:
ApplicationEvent
是所有事件的父类,监听ApplicationEvent
的监听器会接收所有类型的事件。- 自定义事件
UserEvent
的子类UserRegisteredEvent
和UserDeletedEvent
,监听UserEvent
的监听器会接收这两个子类事件。
七、应用场景
Spring 发布订阅模式适用于以下场景:
- 业务解耦:例如用户注册后,需要发送邮件、积分初始化、日志记录等操作,通过事件分离这些逻辑,避免注册服务与其他服务直接耦合。
- 异步通知:耗时操作(如短信发送、报表生成)通过异步事件处理,不阻塞主流程。
- 状态变更通知:如订单状态变更后,通知库存、支付、物流等相关模块。
- 跨组件通信:不同模块(如 Controller、Service、Repository)通过事件交互,无需直接依赖。
总结
Spring 的发布订阅模式基于事件驱动,通过ApplicationEvent
、ApplicationListener
、ApplicationEventPublisher
三大组件实现,配合@EventListener
、@Async
、@TransactionalEventListener
等注解,提供了灵活、解耦的组件通信方式。其核心价值在于降低组件间耦合度,提高系统的可扩展性和可维护性。