前言
大家好,我是大华!这篇文章我们来讲讲Spring
中的两种设计模式:观察者模式和发布订阅模式。
我发现很多文章讲这两个模式时,都是用类似的代码示例,结果读者看完更迷糊了。今天我用完全不同的角度,带大家看清它们的本质区别!
核心概念
首先需要明确:发布订阅模式是观察者模式的一种演进和扩展,两者是同一思想谱系上的不同实现阶段:
- 观察者模式(直接观察):目标对象直接维护观察者列表并通知,结构上有依赖但通过接口解耦
- 发布订阅模式(通过中介):引入事件通道,发布者和订阅者完全解耦,不知彼此存在
下面举例:两种不同的通信模型
观察者模式:像部门内部的晨会
- 经理(被观察者)直接通知团队成员(观察者)
- 面对面沟通,实时确认理解
- 经理知道每个成员的存在,但通过会议议程(接口)规范沟通
发布订阅模式:像城市报纸分发系统
- 报社(发布者)只负责印刷报纸并送到邮局
- 邮局(事件通道)负责分发给各个订阅者
- 报社不知道谁订阅了报纸,订阅者也不知道报纸如何印刷
代码层面的区别
观察者模式:直接调用,强耦合
java
// 1. 观察者接口 - 约定通信契约
public interface OrderObserver {
void onOrderCreated(Order order);
}
// 2. 具体观察者实现
@Component
public class InventoryObserver implements OrderObserver {
@Override
public void onOrderCreated(Order order) {
System.out.println("库存观察者:同步检查库存");
// 必须立即完成,订单创建依赖此结果
}
}
@Component
public class PriceObserver implements OrderObserver {
@Override
public void onOrderCreated(Order order) {
System.out.println("价格观察者:计算最终价格");
// 实时计算,影响订单金额
}
}
// 3. 被观察的主题 - 利用Spring实现松耦合
@Service
public class OrderService {
// Spring自动注入所有OrderObserver实现
@Autowired(required = false)
private List<OrderObserver> observers = Collections.emptyList();
public Order createOrder(Order order) {
// 核心业务逻辑
System.out.println("执行订单创建核心逻辑...");
// 关键特征:同步调用观察者
for (OrderObserver observer : observers) {
observer.onOrderCreated(order); // 阻塞等待每个观察者处理完成
}
System.out.println("所有观察者处理完成,订单创建流程结束");
return order;
}
}
观察者模式的核心特征:
- 接口解耦:通过Observer接口依赖,不依赖具体实现类
- 同步执行:观察者按顺序执行,主线程等待所有处理完成
- 强一致性:所有观察者成功,整个操作才成功
- 实时反馈:可以立即处理观察者的返回值或异常
- 结构依赖:Subject仍然需要维护Observer集合
Spring事件机制:灵活支持两种模式
Spring的事件机制是一个强大的抽象层,根据配置可以体现不同模式的特征:
java
// 1. 事件对象 - 纯数据载体
public class OrderCreatedEvent {
private final Order order;
private final LocalDateTime eventTime;
public OrderCreatedEvent(Order order) {
this.order = order;
this.eventTime = LocalDateTime.now();
}
// getters...
}
// 2. 事件发布者
@Service
public class OrderService {
@Autowired
private ApplicationEventPublisher eventPublisher;
public Order createOrder(Order order) {
// 核心业务逻辑
System.out.println("执行订单创建核心逻辑...");
// 发布事件
eventPublisher.publishEvent(new OrderCreatedEvent(order));
return order;
}
}
模式一:同步事件(更接近观察者模式)
java
@Component
public class SyncInventoryService {
// 默认同步执行 - 发布者线程会阻塞等待此方法完成
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
System.out.println("同步处理库存: " + Thread.currentThread().getName());
// 异常会传播到发布者,影响主流程
}
}
特征:通过ApplicationContext中介,但同步阻塞,更像解耦版的观察者模式。
模式二:异步事件(真正的发布订阅模式)
java
@Component
public class AsyncInventoryService {
@EventListener
@Async // 关键注解,切换到异步模式
public void handleOrderCreated(OrderCreatedEvent event) {
System.out.println("异步处理库存: " + Thread.currentThread().getName());
try {
Thread.sleep(2000); // 模拟耗时操作
System.out.println("库存服务:库存扣减完成");
} catch (Exception e) {
// 异常不会影响主流程
System.out.println("库存处理失败,需要后续补偿");
}
}
}
// 异步配置
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("async-event-");
return executor;
}
}
真正的发布订阅模式特征:
- 完全解耦:发布者不知道订阅者的存在
- 异步处理:不阻塞主业务流程
- 动态扩展:新增订阅者无需修改发布者代码
- 容错性强:单个订阅者失败不影响其他处理
实际项目中的选择标准
选择观察者模式/同步事件当(需要强一致性):
核心业务验证场景:
java
@Service
public class OrderValidationService {
@Autowired
private List<OrderObserver> validators;
public ValidationResult validateOrder(Order order) {
// 需要立即得到所有验证结果
for (OrderObserver validator : validators) {
ValidationResult result = validator.validate(order);
if (!result.isValid()) {
return result; // 实时返回失败
}
}
return ValidationResult.success();
}
}
适用场景:
- 订单创建前的数据校验(库存、价格、风控)
- 业务流程的状态转换验证
- 需要实时反馈的业务规则检查
选择发布订阅模式当(可以最终一致性):
后续业务处理场景:
java
// 使用Spring异步事件(应用内发布订阅)
@Service
public class OrderPostProcessService {
@EventListener
@Async
public void handleOrderSuccess(OrderSuccessEvent event) {
// 这些操作可以异步完成,不影响主流程
sendCongratulationsEmail(event.getOrder());
updateUserLevel(event.getUserId());
syncToDataWarehouse(event.getOrder());
}
}
// 或使用消息中间件(分布式发布订阅)
@Service
public class DistributedNotificationService {
@JmsListener(destination = "order.success.queue")
public void handleOrderSuccess(Order order) {
// 跨服务的异步处理
notifyShippingService(order);
updateBusinessIntelligence(order);
}
}
适用场景:
- 用户注册后的欢迎流程
- 订单支付成功后的后续处理
- 数据同步、日志记录、统计分析
- 微服务间的事件通知
混合使用的最佳实践
在实际复杂业务中,我们通常采用混合模式,根据业务需求选择合适的技术:
java
@Service
public class ComprehensiveOrderService {
@Autowired
private ApplicationEventPublisher eventPublisher;
@Transactional
public Order createOrder(Order order) {
// 阶段一:同步观察者模式 - 强一致性保证
validateOrder(order); // 实时验证(观察者模式)
deductInventory(order); // 实时扣库存(观察者模式)
processPayment(order); // 实时支付(观察者模式)
// 核心业务操作
saveOrder(order);
// 阶段二:应用内发布订阅 - 异步最终一致性
eventPublisher.publishEvent(new OrderCreatedEvent(order));
return order;
}
}
总结
理解这两种模式的关键在于认识它们的演进路径:
- 经典观察者模式:直接依赖,紧耦合
- 接口观察者模式:接口依赖,松耦合
- Spring同步事件:中介解耦,同步执行(观察者模式变体)
- Spring异步事件:中介解耦,异步执行(应用内发布订阅)
简单记忆法则:
- 观察者模式 = "打电话" → 需要对方立即接听并回应
- 发布订阅模式 = "发邮件" → 发送后继续工作,对方稍后处理
现在你应该能清楚区分这两种模式了吧?希望这篇文章能帮助你在实际项目中,灵活的选择合适的技术方案。
本文首发于公众号:程序员刘大华,专注分享前后端开发的实战笔记。关注我,少走弯路,一起进步!
📌往期精彩
《SpringBoot 中的 7 种耗时统计方式,你用过几种?》