观察者模式

观察者模式

lx 4 2026-07-06

观察者模式-实现

在 Spring 中,这个基于观察者模式设计的机制,通常被称为 Spring 事件驱动机制ApplicationEvent 机制。它本质上是观察者模式在 Spring 框架内的一种优雅实现,用于实现应用内部各组件间的松耦合通信。

核心三要素

这套机制围绕三个核心角色展开:

  1. 事件(Event):信息的载体,相当于观察者模式中的被观察者。它封装了业务数据,比如一次订单支付、一个用户注册动作,都需要对应的事件类。
  2. 事件发布者(Publisher):事件的触发者。它不需要知道谁会处理事件,只需将事件发布到Spring容器中。角色上类似于观察者模式中的主题(Subject)
  3. 事件监听者(Listener):事件的响应者。它监听并处理感兴趣的事件,对应于观察者模式中的观察者(Observer)。每个监听器只关心自己订阅的事件类型,当事件发生时执行相应的业务逻辑。

如何使用(分步详解)

通过一个“用户注册”的经典场景来梳理具体的使用步骤

第一步:定义事件(事件类)

首先,创建一个类来表示“用户注册成功”这件事。推荐直接继承 ApplicationEvent类,也可以使用普通POJO,但继承是更标准的方式。

// 1. 自定义事件,继承 ApplicationEvent
public class UserRegisteredEvent extends ApplicationEvent {
    private final String userEmail;

    // 构造方法,source是事件源,通常传this即可
    public UserRegisteredEvent(Object source, String userEmail) {
        super(source);
        this.userEmail = userEmail;
    }

    // 提供getter方法,让监听器能获取数据
    public String getUserEmail() {
        return userEmail;
    }
}

第二步:发布事件(发布者)

在核心业务逻辑里注入 ApplicationEventPublisher,然后在合适的时机(比如用户数据保存成功后)调用 publishEvent()方法把事件发出去。

@Service
public class UserService {

    @Autowired
    private ApplicationEventPublisher eventPublisher; // 注入事件发布器

    public void registerUser(String email) {
        // 1. 核心业务逻辑:保存用户信息到数据库...
        System.out.println("用户 " + email + " 注册成功!");

        // 2. 发布事件:把“注册成功”这件事广播出去
        UserRegisteredEvent event = new UserRegisteredEvent(this, email);
        eventPublisher.publishEvent(event); 
        // 发布后,所有监听这个事件的监听器就会自动执行
    }
}

第三步:监听事件并处理(监听者)

这是最灵活的环节,Spring 提供了两种常用的监听方式。

方式一:使用 @EventListener 注解(最推荐)

这是最简洁、最现代的方式,无需实现任何接口,在方法上标注注解即可。

@Component
public class NotificationService {

    // 方法参数即为要监听的事件类型
    @EventListener
    public void handleUserRegistration(UserRegisteredEvent event) {
        // 处理逻辑:比如发送欢迎邮件
        String email = event.getUserEmail();
        System.out.println("📧 正在给 " + email + " 发送欢迎邮件...");
        // 实际项目里这里会调用邮件服务
    }
}
方式二:实现 ApplicationListener 接口

这是一种更传统的方式,可以实现接口,并重写 onApplicationEvent方法。

@Component
public class CouponListener implements ApplicationListener<UserRegisteredEvent> {

    @Override
    public void onApplicationEvent(UserRegisteredEvent event) {
        // 处理逻辑:比如赠送新人优惠券
        String email = event.getUserEmail();
        System.out.println("🎁 给 " + email + " 赠送一张新人优惠券!");
    }
}

深入一点:高级特性

  • 异步处理(@Async:默认事件是同步处理的,发布者线程会等待所有监听器执行完毕。如果某个监听器逻辑耗时(如发送邮件),可以结合 @Async注解实现异步处理,避免阻塞主流程。使用时需要在配置类上添加 @EnableAsync开启支持。
  • 条件过滤(condition:可以给 @EventListener添加 condition属性,通过SpEL表达式控制监听器在满足特定条件时才执行。例如,只处理特定域名的用户注册事件。

总结:为什么用它?

  1. 解耦:发布者(UserService)和监听者(NotificationServiceCouponListener)完全不知道对方的存在,业务逻辑清晰,修改一方不会影响另一方。
  2. 扩展性强:未来想增加一个新功能,比如“用户注册后记录日志”,只需新增一个监听器,无需改动 UserService的代码,符合开闭原则。
  3. 灵活性高:支持同步、异步、带条件监听等多种处理方式,能应对复杂业务场景。

可以说,这套事件机制是构建高内聚、低耦合Spring应用的一把利器。

注意点

1.@TransactionalEventListener

// @Transactional + @EventListener:无法控制时机
@Component
class Listener1 {
    @Transactional
    @EventListener
    public void handle(OrderPaidEvent event) {
        // ⚠️ 事件发布后立即执行
        // ⚠️ 无法延迟到事务提交后
    }
}

// @TransactionalEventListener:精确控制时机
@Component
class Listener2 {
  
    @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
    public void beforeCommit(OrderPaidEvent event) {
        // ✅ 事务提交前执行(可以做最后验证)
        // ⚠️ 抛异常会让事务回滚
    }
  
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void afterCommit(OrderPaidEvent event) {
        // ✅ 事务提交后执行(最常用)
        // ✅ 可以安全发送消息、邮件
    }
  
    @TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
    public void afterRollback(OrderPaidEvent event) {
        // ✅ 事务回滚后执行(记录失败日志)
    }
  
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION)
    public void afterCompletion(OrderPaidEvent event) {
        // ✅ 事务完成后执行(无论成功还是失败)
    }
}

2.发布-订阅模式(Pub-Sub)

  • 观察者模式被观察者(Subject) 直接持有 观察者(Observer) 的引用,并直接调用其方法。它们是直接通信的。(服务内)
  • 发布-订阅模式发布者(Publisher)订阅者(Subscriber) 互不知道对方,它们通过一个 消息代理(Broker,即中间人) 来通信。发布者把消息发给Broker,Broker再转发给订阅者。(不同服务间)

都是为了解耦

不能用的场景

使用场景

  • 操作必须和主流程强一致(必须在同一个事务中)

  • 操作极其简单

  • 需要返回值

  • 用户注册/登录后的系列操作

    @Service
    public class UserService {
    
        @Transactional
        public User register(UserRegisterDTO dto) {
            // 1. 核心逻辑:保存用户
            User user = userRepository.save(new User(dto));
    
            // 2. 发布事件(触发各种副作用)
            eventPublisher.publishEvent(new UserRegisteredEvent(this, user));
    
            return user;
        }
    }
    
    // 多个监听器各司其职
    @Component
    public class UserEventListeners {
    
        @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
        public void sendWelcomeEmail(UserRegisteredEvent event) {
            // 发送欢迎邮件(耗时操作)
            emailService.send(event.getUser().getEmail(), "欢迎注册!");
        }
    
        @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
        public void initDefaultData(UserRegisteredEvent event) {
            // 初始化用户默认配置
            configService.initDefaultConfig(event.getUser().getId());
        }
    
        @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
        public void sendCoupon(UserRegisteredEvent event) {
            // 赠送新人优惠券
            couponService.giveNewUserCoupon(event.getUser().getId());
        }
    
        @EventListener
        public void updateCache(UserRegisteredEvent event) {
            // 更新缓存(非关键,失败不影响)
            cacheService.put("user:" + event.getUser().getId(), event.getUser());
        }
    }
    
  • 日志记录和审计

    @Service
    public class ProductService {
    
        @Transactional
        public void updatePrice(Long productId, BigDecimal price) {
            // 核心逻辑:更新价格
            productRepository.updatePrice(productId, price);
    
            // 发布事件(日志记录)
            eventPublisher.publishEvent(new ProductPriceChangedEvent(this, productId, price));
        }
    }
    
    @Component
    public class AuditListener {
    
        @EventListener
        public void logPriceChange(ProductPriceChangedEvent event) {
            // 记录审计日志(不依赖事务,失败不影响主流程)
            auditLogService.save(AuditLog.builder()
                .operation("修改价格")
                .productId(event.getProductId())
                .newPrice(event.getNewPrice())
                .operator(SecurityContextHolder.getContext().getAuthentication().getName())
                .build());
        }
    }
    
  • 缓存更新/清除

    @Service
    public class ArticleService {
    
        @Transactional
        public void updateArticle(Long id, String content) {
            // 核心逻辑:更新文章
            articleRepository.updateContent(id, content);
    
            // 发布事件
            eventPublisher.publishEvent(new ArticleUpdatedEvent(this, id));
        }
    }
    
    @Component
    public class CacheListener {
    
        @EventListener
        public void clearArticleCache(ArticleUpdatedEvent event) {
            // 清除缓存(失败不影响主流程)
            redisTemplate.delete("article:" + event.getArticleId());
            redisTemplate.delete("article:list:" + event.getUserId());
        }
    }
    
  • 事件驱动的微服务通信(配合消息队列)

    @Service
    public class OrderService {
    
        @Transactional
        public void createOrder(OrderDTO dto) {
            // 1. 创建订单
            Order order = orderRepository.save(new Order(dto));
    
            // 2. 发布本地事件
            eventPublisher.publishEvent(new OrderCreatedEvent(this, order));
        }
    }
    // 主要就是用来解耦 便于换中间件 且 事务提交后才发送消息
    @Component
    public class EventBridgeListener {
    
        @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
        public void sendToKafka(OrderCreatedEvent event) {
            // 事务提交后,发送消息到Kafka 
            kafkaTemplate.send("order-topic", event.getOrder());
            // 其他微服务(库存、支付、通知)会消费这个消息
        }
    }
    

缺点

  • 事件链路“不可见”
  • 监听器执行顺序“不可靠”
  • 异常处理“一片狼藉”
  • 无界的事件队列(异步场景)