Spring事件机制:解耦利器与实战

Spring事件机制:解耦利器与实战

一、为什么需要事件机制?

在传统的业务开发中,我们经常会遇到这样的场景:

scss 复制代码
// 传统方式:强耦合
@Service
public class OrderService {

    @Autowired
    private EmailService emailService;

    @Autowired
    private SmsService smsService;

    @Autowired
    private InventoryService inventoryService;

    @Autowired
    private PointsService pointsService;

    public void createOrder(Order order) {
        // 1. 保存订单
        orderRepository.save(order);

        // 2. 发送邮件
        emailService.sendOrderConfirmation(order);

        // 3. 发送短信
        smsService.sendNotification(order);

        // 4. 扣减库存
        inventoryService.deductStock(order);

        // 5. 增加积分
        pointsService.addPoints(order.getUserId(), order.getAmount());
    }
}

这种方式存在严重问题:

  • 强耦合:OrderService依赖太多服务
  • 难维护:新增业务需要修改OrderService
  • 不灵活:无法动态添加/移除业务逻辑
  • 性能差:所有操作同步执行

Spring事件机制优雅地解决了这些问题!

二、Spring事件机制核心概念

Spring事件机制基于观察者模式,包含三个核心角色:

1. 事件(Event)

  • 继承ApplicationEvent的POJO类
  • 携带业务数据

2. 事件发布者(Publisher)

  • 通过ApplicationEventPublisher发布事件
  • 通常是业务服务类

3. 事件监听器(Listener)

  • 使用@EventListener或实现ApplicationListener
  • 处理特定类型的事件

工作流程:

rust 复制代码
发布者 --发布事件--> Spring容器 --分发--> 监听器1
                                  └--分发--> 监听器2
                                  └--分发--> 监听器3

三、基础使用

3.1 定义事件

scala 复制代码
/**
 * 订单创建事件
 */
public class OrderCreatedEvent extends ApplicationEvent {

    private final Order order;

    public OrderCreatedEvent(Object source, Order order) {
        super(source);
        this.order = order;
    }

    public Order getOrder() {
        return order;
    }
}

3.2 发布事件

scss 复制代码
/**
 * 订单服务 - 事件发布者
 */
@Service
public class OrderService {

    private final ApplicationEventPublisher eventPublisher;
    private final OrderRepository orderRepository;

    public OrderService(ApplicationEventPublisher eventPublisher,
                       OrderRepository orderRepository) {
        this.eventPublisher = eventPublisher;
        this.orderRepository = orderRepository;
    }

    public Order createOrder(OrderRequest request) {
        // 1. 核心业务:保存订单
        Order order = new Order();
        order.setUserId(request.getUserId());
        order.setAmount(request.getAmount());
        order = orderRepository.save(order);

        // 2. 发布事件
        eventPublisher.publishEvent(new OrderCreatedEvent(this, order));

        return order;
    }
}

3.3 监听事件

csharp 复制代码
/**
 * 邮件服务 - 事件监听器
 */
@Component
public class EmailEventListener {

    private final EmailService emailService;

    public EmailEventListener(EmailService emailService) {
        this.emailService = emailService;
    }

    @EventListener
    public void handleOrderCreated(OrderCreatedEvent event) {
        Order order = event.getOrder();
        emailService.sendOrderConfirmation(order);
        System.out.println("邮件已发送:订单号 " + order.getId());
    }
}

/**
 * 短信服务 - 事件监听器
 */
@Component
public class SmsEventListener {

    private final SmsService smsService;

    public SmsEventListener(SmsService smsService) {
        this.smsService = smsService;
    }

    @EventListener
    public void handleOrderCreated(OrderCreatedEvent event) {
        Order order = event.getOrder();
        smsService.sendNotification(order);
        System.out.println("短信已发送:订单号 " + order.getId());
    }
}

/**
 * 库存服务 - 事件监听器
 */
@Component
public class InventoryEventListener {

    private final InventoryService inventoryService;

    public InventoryEventListener(InventoryService inventoryService) {
        this.inventoryService = inventoryService;
    }

    @EventListener
    public void handleOrderCreated(OrderCreatedEvent event) {
        Order order = event.getOrder();
        inventoryService.deductStock(order);
        System.out.println("库存已扣减:订单号 " + order.getId());
    }
}

效果对比:

  • OrderService只负责核心业务,完全解耦
  • 新增监听器无需修改OrderService
  • 监听器可以动态添加/移除

四、高级特性

4.1 异步事件

默认情况下,事件是同步处理的。使用@Async可以异步处理:

less 复制代码
/**
 * 开启异步支持
 */
@Configuration
@EnableAsync
public class AsyncConfig {

    @Bean
    public Executor asyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("event-async-");
        executor.initialize();
        return executor;
    }
}

/**
 * 异步监听器
 */
@Component
public class AsyncEventListener {

    @EventListener
    @Async
    public void handleOrderCreatedAsync(OrderCreatedEvent event) {
        System.out.println("异步处理开始,线程:" + Thread.currentThread().getName());

        // 耗时操作
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("异步处理完成");
    }
}

4.2 条件监听

使用SpEL表达式实现条件监听:

csharp 复制代码
@Component
public class ConditionalEventListener {

    // 只处理金额大于1000的订单
    @EventListener(condition = "#event.order.amount > 1000")
    public void handleLargeOrder(OrderCreatedEvent event) {
        System.out.println("处理大额订单:" + event.getOrder().getAmount());
    }

    // 只处理VIP用户的订单
    @EventListener(condition = "#event.order.userType == 'VIP'")
    public void handleVipOrder(OrderCreatedEvent event) {
        System.out.println("处理VIP订单");
    }
}

4.3 事件监听顺序

使用@Order控制监听器执行顺序:

java 复制代码
@Component
public class OrderedEventListener {

    @EventListener
    @Order(1)
    public void firstListener(OrderCreatedEvent event) {
        System.out.println("第一个执行");
    }

    @EventListener
    @Order(2)
    public void secondListener(OrderCreatedEvent event) {
        System.out.println("第二个执行");
    }

    @EventListener
    @Order(3)
    public void thirdListener(OrderCreatedEvent event) {
        System.out.println("第三个执行");
    }
}

4.4 泛型事件

Spring 4.2+支持泛型事件:

typescript 复制代码
/**
 * 泛型事件基类
 */
public class EntityEvent<T> extends ApplicationEvent {

    private final T entity;
    private final EventType type;

    public EntityEvent(Object source, T entity, EventType type) {
        super(source);
        this.entity = entity;
        this.type = type;
    }

    public T getEntity() {
        return entity;
    }

    public EventType getType() {
        return type;
    }

    public enum EventType {
        CREATED, UPDATED, DELETED
    }
}

/**
 * 泛型监听器
 */
@Component
public class GenericEventListener {

    // 只监听User类型的创建事件
    @EventListener
    public void handleUserCreated(EntityEvent<User> event) {
        if (event.getType() == EntityEvent.EventType.CREATED) {
            System.out.println("用户创建:" + event.getEntity().getName());
        }
    }

    // 只监听Product类型的事件
    @EventListener
    public void handleProductEvent(EntityEvent<Product> event) {
        System.out.println("产品事件:" + event.getType());
    }
}

4.5 事务事件

Spring提供了事务事件监听:

java 复制代码
@Component
public class TransactionalEventListener {

    // 事务提交后执行
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handleAfterCommit(OrderCreatedEvent event) {
        System.out.println("事务已提交,发送通知");
    }

    // 事务回滚后执行
    @TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
    public void handleAfterRollback(OrderCreatedEvent event) {
        System.out.println("事务已回滚,记录日志");
    }

    // 事务完成后执行(无论提交还是回滚)
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION)
    public void handleAfterCompletion(OrderCreatedEvent event) {
        System.out.println("事务已完成");
    }

    // 事务提交前执行
    @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
    public void handleBeforeCommit(OrderCreatedEvent event) {
        System.out.println("事务即将提交");
    }
}

五、实战应用场景

5.1 用户注册场景

java 复制代码
/**
 * 用户注册事件
 */
public class UserRegisteredEvent extends ApplicationEvent {

    private final User user;

    public UserRegisteredEvent(Object source, User user) {
        super(source);
        this.user = user;
    }

    public User getUser() {
        return user;
    }
}

/**
 * 用户服务
 */
@Service
public class UserService {

    private final ApplicationEventPublisher eventPublisher;
    private final UserRepository userRepository;

    public UserService(ApplicationEventPublisher eventPublisher,
                      UserRepository userRepository) {
        this.eventPublisher = eventPublisher;
        this.userRepository = userRepository;
    }

    @Transactional
    public User registerUser(UserRegisterRequest request) {
        // 1. 创建用户
        User user = new User();
        user.setUsername(request.getUsername());
        user.setEmail(request.getEmail());
        user.setPassword(encodePassword(request.getPassword()));
        user = userRepository.save(user);

        // 2. 发布事件
        eventPublisher.publishEvent(new UserRegisteredEvent(this, user));

        return user;
    }

    private String encodePassword(String password) {
        // 密码加密逻辑
        return password;
    }
}

/**
 * 欢迎邮件监听器
 */
@Component
public class WelcomeEmailListener {

    @EventListener
    @Async
    public void sendWelcomeEmail(UserRegisteredEvent event) {
        User user = event.getUser();
        // 发送欢迎邮件
        System.out.println("发送欢迎邮件给:" + user.getEmail());
    }
}

/**
 * 赠送新人礼包监听器
 */
@Component
public class NewUserGiftListener {

    @EventListener
    @Async
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void giveNewUserGift(UserRegisteredEvent event) {
        User user = event.getUser();
        // 赠送新人礼包
        System.out.println("赠送新人礼包给用户:" + user.getUsername());
    }
}

/**
 * 初始化用户配置监听器
 */
@Component
public class UserConfigInitListener {

    @EventListener
    @Order(1)
    public void initUserConfig(UserRegisteredEvent event) {
        User user = event.getUser();
        // 初始化用户配置
        System.out.println("初始化用户配置:" + user.getId());
    }
}

5.2 文章发布场景

less 复制代码
/**
 * 文章发布事件
 */
public class ArticlePublishedEvent extends ApplicationEvent {

    private final Article article;

    public ArticlePublishedEvent(Object source, Article article) {
        super(source);
        this.article = article;
    }

    public Article getArticle() {
        return article;
    }
}

/**
 * 文章服务
 */
@Service
public class ArticleService {

    private final ApplicationEventPublisher eventPublisher;

    @Transactional
    public void publishArticle(Long articleId) {
        // 1. 更新文章状态为已发布
        Article article = articleRepository.findById(articleId).orElseThrow();
        article.setStatus(ArticleStatus.PUBLISHED);
        article.setPublishTime(LocalDateTime.now());
        articleRepository.save(article);

        // 2. 发布事件
        eventPublisher.publishEvent(new ArticlePublishedEvent(this, article));
    }
}

/**
 * 搜索索引更新监听器
 */
@Component
public class SearchIndexListener {

    @EventListener
    @Async
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void updateSearchIndex(ArticlePublishedEvent event) {
        Article article = event.getArticle();
        // 更新ElasticSearch索引
        System.out.println("更新搜索索引:" + article.getTitle());
    }
}

/**
 * 缓存预热监听器
 */
@Component
public class CacheWarmUpListener {

    @EventListener
    @Async
    public void warmUpCache(ArticlePublishedEvent event) {
        Article article = event.getArticle();
        // 预热Redis缓存
        System.out.println("预热缓存:" + article.getId());
    }
}

/**
 * 通知订阅者监听器
 */
@Component
public class SubscriberNotificationListener {

    @EventListener
    @Async
    public void notifySubscribers(ArticlePublishedEvent event) {
        Article article = event.getArticle();
        // 通知订阅该作者的用户
        System.out.println("通知订阅者:新文章《" + article.getTitle() + "》");
    }
}

5.3 支付成功场景

less 复制代码
/**
 * 支付成功事件
 */
public class PaymentSuccessEvent extends ApplicationEvent {

    private final Payment payment;

    public PaymentSuccessEvent(Object source, Payment payment) {
        super(source);
        this.payment = payment;
    }

    public Payment getPayment() {
        return payment;
    }
}

/**
 * 支付服务
 */
@Service
public class PaymentService {

    private final ApplicationEventPublisher eventPublisher;

    @Transactional
    public void processPayment(PaymentRequest request) {
        // 1. 处理支付
        Payment payment = new Payment();
        payment.setOrderId(request.getOrderId());
        payment.setAmount(request.getAmount());
        payment.setStatus(PaymentStatus.SUCCESS);
        paymentRepository.save(payment);

        // 2. 发布事件
        eventPublisher.publishEvent(new PaymentSuccessEvent(this, payment));
    }
}

/**
 * 订单状态更新监听器
 */
@Component
public class OrderStatusUpdateListener {

    @EventListener
    @Order(1)
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void updateOrderStatus(PaymentSuccessEvent event) {
        Payment payment = event.getPayment();
        // 更新订单状态为已支付
        System.out.println("更新订单状态:" + payment.getOrderId());
    }
}

/**
 * 发货通知监听器
 */
@Component
public class ShipmentListener {

    @EventListener
    @Order(2)
    @Async
    public void notifyWarehouse(PaymentSuccessEvent event) {
        Payment payment = event.getPayment();
        // 通知仓库发货
        System.out.println("通知仓库发货:订单" + payment.getOrderId());
    }
}

/**
 * 积分奖励监听器
 */
@Component
public class PointsRewardListener {

    @EventListener
    @Async
    public void awardPoints(PaymentSuccessEvent event) {
        Payment payment = event.getPayment();
        // 根据支付金额奖励积分
        int points = payment.getAmount().intValue() / 10;
        System.out.println("奖励积分:" + points);
    }
}

六、与消息队列对比

特性 Spring事件机制 消息队列(RabbitMQ/Kafka)
作用范围 单应用内 跨应用、分布式
持久化 不支持 支持
可靠性 一般
性能 高(内存) 中等(网络IO)
学习成本 中等
适用场景 应用内解耦 微服务间通信

使用建议:

  • 单体应用内部解耦 → Spring事件机制
  • 微服务间通信 → 消息队列
  • 两者可以结合使用

七、最佳实践

7.1 推荐做法

1. 事件命名规范

scala 复制代码
// ✅ 推荐:使用过去式,表示已发生的事件
public class OrderCreatedEvent extends ApplicationEvent { }
public class PaymentSuccessEvent extends ApplicationEvent { }
public class UserRegisteredEvent extends ApplicationEvent { }

// ❌ 不推荐
public class CreateOrderEvent extends ApplicationEvent { }
public class PayEvent extends ApplicationEvent { }

2. 事件数据封装

scala 复制代码
// ✅ 推荐:封装必要的业务数据
public class OrderCreatedEvent extends ApplicationEvent {
    private final Order order;
    private final Long userId;
    private final LocalDateTime createTime;

    // 构造器、getter
}

// ❌ 不推荐:只传递ID
public class OrderCreatedEvent extends ApplicationEvent {
    private final Long orderId;  // 监听器需要再查询数据库
}

3. 异步处理非核心业务

less 复制代码
// ✅ 推荐:非核心业务异步处理
@Component
public class NotificationListener {

    @EventListener
    @Async
    public void sendNotification(OrderCreatedEvent event) {
        // 发送通知是非核心业务,异步处理
    }
}

// ❌ 不推荐:核心业务异步处理
@Component
public class PaymentListener {

    @EventListener
    @Async  // 支付是核心业务,不应异步
    public void processPayment(OrderCreatedEvent event) {
        // 处理支付
    }
}

4. 使用事务事件监听

less 复制代码
// ✅ 推荐:需要数据库持久化的操作使用事务事件
@Component
public class StockListener {

    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void deductStock(OrderCreatedEvent event) {
        // 确保订单事务提交后才扣减库存
    }
}

// ❌ 不推荐:可能导致数据不一致
@Component
public class StockListener {

    @EventListener
    public void deductStock(OrderCreatedEvent event) {
        // 订单事务可能回滚,但库存已扣减
    }
}

7.2 避免的陷阱

1. 避免循环事件

typescript 复制代码
// ❌ 危险:可能导致无限循环
@Component
public class EventA {
    @Autowired
    private ApplicationEventPublisher publisher;

    @EventListener
    public void handleB(EventB event) {
        publisher.publishEvent(new EventA());  // 触发EventA
    }
}

@Component
public class EventB {
    @Autowired
    private ApplicationEventPublisher publisher;

    @EventListener
    public void handleA(EventA event) {
        publisher.publishEvent(new EventB());  // 触发EventB,形成循环!
    }
}

// ✅ 解决方案:添加防重标记
public class EventA extends ApplicationEvent {
    private boolean processed = false;
}

2. 避免监听器中的长时间阻塞

typescript 复制代码
// ❌ 不推荐:同步监听器执行耗时操作
@Component
public class SlowListener {

    @EventListener
    public void slowProcess(OrderCreatedEvent event) {
        Thread.sleep(10000);  // 阻塞10秒
        // 所有后续监听器都要等待
    }
}

// ✅ 推荐:耗时操作异步处理
@Component
public class FastListener {

    @EventListener
    @Async
    public void asyncProcess(OrderCreatedEvent event) {
        Thread.sleep(10000);  // 不阻塞其他监听器
    }
}

八、性能优化

8.1 使用异步处理

typescript 复制代码
@Configuration
@EnableAsync
public class AsyncEventConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(20);
        executor.setQueueCapacity(200);
        executor.setThreadNamePrefix("event-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return (ex, method, params) -> {
            log.error("异步事件处理异常: method={}, params={}",
                     method.getName(), Arrays.toString(params), ex);
        };
    }
}

8.2 批量处理事件

csharp 复制代码
/**
 * 批量处理事件
 */
@Component
public class BatchEventProcessor {

    private final List<OrderCreatedEvent> eventQueue = new CopyOnWriteArrayList<>();
    private static final int BATCH_SIZE = 100;

    @EventListener
    public void collectEvent(OrderCreatedEvent event) {
        eventQueue.add(event);

        if (eventQueue.size() >= BATCH_SIZE) {
            processBatch();
        }
    }

    @Scheduled(fixedDelay = 5000)
    public void processBatch() {
        if (eventQueue.isEmpty()) {
            return;
        }

        List<OrderCreatedEvent> batch = new ArrayList<>(eventQueue);
        eventQueue.clear();

        // 批量处理
        System.out.println("批量处理 " + batch.size() + " 个事件");
    }
}

8.3 监控事件处理

java 复制代码
/**
 * 事件处理监控
 */
@Aspect
@Component
public class EventListenerMonitor {

    @Around("@annotation(org.springframework.context.event.EventListener)")
    public Object monitorEventListener(ProceedingJoinPoint pjp) throws Throwable {
        String methodName = pjp.getSignature().getName();
        long startTime = System.currentTimeMillis();

        try {
            Object result = pjp.proceed();
            long duration = System.currentTimeMillis() - startTime;

            if (duration > 1000) {
                log.warn("事件监听器执行缓慢: method={}, duration={}ms",
                        methodName, duration);
            }

            return result;
        } catch (Exception e) {
            log.error("事件监听器执行失败: method={}", methodName, e);
            throw e;
        }
    }
}

九、总结

Spring事件机制是一个强大的解耦工具,合理使用能够显著提升代码的可维护性和扩展性。

核心要点:

  1. 解耦业务:发布者和监听器完全解耦
  2. 灵活扩展:新增监听器无需修改发布者
  3. 异步处理:支持异步处理提升性能
  4. 事务集成:与Spring事务无缝集成

适用场景:

  • ✅ 单体应用内部解耦
  • ✅ 一对多的业务通知
  • ✅ 非核心业务异步处理
  • ✅ 审计日志、统计分析

不适用场景:

  • ❌ 微服务间通信(用消息队列)
  • ❌ 需要持久化的消息(用消息队列)
  • ❌ 强一致性要求(同步调用)
相关推荐
float_六七7 小时前
Spring AOP连接点实战解析
java·后端·spring
醇氧7 小时前
springAI学习 一
学习·spring·ai·ai编程
老华带你飞7 小时前
出行旅游安排|基于springboot出行旅游安排系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·spring·旅游
m0_740043738 小时前
SpringBoot快速入门01- Spring 的 IOC/DI、AOP,
spring boot·后端·spring
老华带你飞8 小时前
垃圾分类|基于springboot 垃圾分类系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·spring
醇氧8 小时前
在 Spring Service 中使用 private final Gson gson = new Gson(); 是否安全?
java·安全·spring
默 语9 小时前
Spring-AI vs LangChain4J:Java生态的AI框架选型指南
java·人工智能·spring·ai·langchain·langchain4j·spring-ai
小花10 小时前
SpringMvc中的拦截器
java·spring·springmvc
xing-xing10 小时前
Java大模型开发框架Spring AI
java·人工智能·spring