ApplicationEvent事件

Spring Boot 事件处理完整实例代码

本文将通过一个完整的用户注册示例,详细展示Spring Boot事件处理机制的各个方面,包括事件定义、发布、监听、异步处理、事务支持和异常处理等核心概念。

1. 事件流程图

流程图

2. 事件处理失败机制详解

在Spring Boot事件处理中,当事件发布后处理失败时,会根据不同的监听器类型产生不同的影响。理解这些失败机制对于设计健壮的事件驱动架构至关重要。

2.1 同步监听器处理失败

2.1.1 @EventListener (同步)

@EventListener
public void handleUserRegistered(UserRegisteredEvent event) {
    // 如果这里抛出异常
    throw new RuntimeException("邮件发送失败");
}

失败影响:

  • 异常会传播到事件发布者
  • 整个事务会回滚(如果在事务中)
  • 后续监听器不会执行
  • 用户注册操作失败

2.1.2 ApplicationListener (同步)

@Component
public class UserListener implements ApplicationListener<UserRegisteredEvent> {
    @Override
    public void onApplicationEvent(UserRegisteredEvent event) {
        // 异常同样会传播
        throw new RuntimeException("审计日志记录失败");
    }
}

失败影响: 与@EventListener相同,异常会中断整个流程

2.2 异步监听器处理失败

2.2.1 @Async + @EventListener

@EventListener
@Async
public void handleUserRegisteredAsync(UserRegisteredEvent event) {
    // 异步处理中的异常
    throw new RuntimeException("统计分析失败");
}

失败影响:

  • 异常不会传播到事件发布者
  • 主流程继续执行
  • 用户注册成功
  • ⚠️ 异常被AsyncUncaughtExceptionHandler处理

2.2.2 异步异常处理配置

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
    
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return (throwable, method, objects) -> {
            logger.error("异步事件处理失败 - 方法: {}, 参数: {}", 
                        method.getName(), objects, throwable);
            // 可以在这里进行补偿处理
            // 比如:重试、记录失败日志、发送告警等
            handleAsyncFailure(throwable, method, objects);
        };
    }
    
    private void handleAsyncFailure(Throwable throwable, Method method, Object[] objects) {
        // 补偿处理逻辑
        if (objects.length > 0 && objects[0] instanceof UserRegisteredEvent) {
            UserRegisteredEvent event = (UserRegisteredEvent) objects[0];
            // 加入重试队列或发送告警
            retryService.addToRetryQueue(event, method.getName(), throwable);
        }
    }
}

2.3 事务监听器处理失败

2.3.1 @TransactionalEventListener

@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleAfterCommit(UserRegisteredEvent event) {
    throw new RuntimeException("缓存更新失败");
}

失败影响:

  • 主事务已经提交,不会回滚
  • 异常会被记录,但不影响主流程
  • ⚠️ 需要手动处理补偿逻辑

2.4 实际场景示例

2.4.1 用户注册完整流程

@Service
@Transactional
public class UserService {
    
    public User registerUser(String username, String email) {
        try {
            // 1. 保存用户到数据库
            User user = saveUser(username, email);
            
            // 2. 发布事件
            UserRegisteredEvent event = new UserRegisteredEvent(this, user);
            eventPublisher.publishEvent(event);
            
            return user; // 如果同步监听器失败,这里不会执行到
            
        } catch (Exception e) {
            logger.error("用户注册失败", e);
            throw e; // 事务回滚
        }
    }
}

2.4.2 不同监听器的失败场景

点击展开:场景1 - 邮件发送失败(同步,关键业务)
@EventListener
@Order(1)
public void sendWelcomeEmail(UserRegisteredEvent event) {
    try {
        emailService.sendEmail(event.getUser().getEmail());
        logger.info("欢迎邮件发送成功");
        
    } catch (Exception e) {
        logger.error("邮件发送失败", e);
        // 如果邮件发送是关键业务,重新抛出异常使整个注册失败
        throw new BusinessException("用户注册失败:无法发送欢迎邮件", e);
    }
}

结果: 用户注册失败,数据库事务回滚

点击展开:场景2 - 统计更新失败(异步,非关键业务)
@EventListener
@Async
@Order(2)
public void updateStatistics(UserRegisteredEvent event) {
    try {
        statisticsService.updateUserCount();
        logger.info("用户统计更新成功");
        
    } catch (Exception e) {
        logger.error("统计更新失败", e);
        // 异步处理,异常不会影响主流程
        // 可以加入重试队列
        retryQueue.add(new StatisticsRetryTask(event));
    }
}

结果: 用户注册成功,统计更新失败但不影响主业务

点击展开:场景3 - 缓存更新失败(事务后处理)
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void updateCache(UserRegisteredEvent event) {
    try {
        cacheService.updateUserCache(event.getUser());
        logger.info("用户缓存更新成功");
        
    } catch (Exception e) {
        logger.error("缓存更新失败", e);
        // 主事务已提交,需要补偿处理
        handleCacheUpdateFailure(event.getUser(), e);
    }
}

private void handleCacheUpdateFailure(User user, Exception e) {
    // 补偿策略
    // 1. 记录失败日志
    failureLogService.logCacheFailure(user.getId(), e);
    
    // 2. 加入重试队列
    cacheRetryQueue.add(new CacheUpdateTask(user));
    
    // 3. 发送告警
    alertService.sendCacheFailureAlert(user.getId(), e.getMessage());
}

结果: 用户注册成功,缓存更新失败需要补偿处理

2.5 最佳实践建议

2.5.1 异常处理策略

@EventListener
public void handleUserRegistered(UserRegisteredEvent event) {
    try {
        // 业务处理逻辑
        emailService.sendWelcomeEmail(event.getUser());
        
    } catch (Exception e) {
        logger.error("发送欢迎邮件失败: {}", event.getUser().getEmail(), e);
        
        // 根据业务需求决定是否重新抛出异常
        if (isEmailCritical()) {
            // 如果是关键流程,重新抛出异常使整个操作失败
            throw new BusinessException("关键业务处理失败", e);
        } else {
            // 如果是非关键流程,记录日志并进行补偿处理
            handleEmailFailure(event.getUser(), e);
        }
    }
}

private void handleEmailFailure(User user, Exception e) {
    // 补偿处理
    emailRetryService.scheduleRetry(user.getEmail(), e);
    notificationService.notifyAdminOfEmailFailure(user, e);
}

2.5.2 监听器分类处理

// 关键业务逻辑 - 同步处理,失败则整体失败
@EventListener
@Order(1)
public void criticalBusinessLogic(UserRegisteredEvent event) {
    try {
        // 必须成功的业务逻辑
        criticalService.process(event.getUser());
    } catch (Exception e) {
        throw new BusinessException("关键业务处理失败", e);
    }
}

// 非关键业务逻辑 - 异步处理,失败不影响主流程
@EventListener
@Async
@Order(2)
public void nonCriticalBusinessLogic(UserRegisteredEvent event) {
    try {
        // 可以失败的业务逻辑
        analyticsService.updateUserAnalytics(event.getUser());
    } catch (Exception e) {
        logger.warn("非关键业务处理失败", e);
        // 加入重试队列或忽略
    }
}

// 数据一致性要求 - 事务后处理
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void dataConsistencyLogic(UserRegisteredEvent event) {
    try {
        // 需要在事务提交后执行的逻辑
        searchIndexService.indexUser(event.getUser());
    } catch (Exception e) {
        // 需要补偿处理
        handleIndexFailure(event.getUser(), e);
    }
}

2.5.3 补偿机制实现

@Component
public class EventFailureHandler {
    
    private final RetryService retryService;
    private final AlertService alertService;
    
    @EventListener
    public void handleEvent(UserRegisteredEvent event) {
        try {
            // 正常处理逻辑
            processEvent(event);
            
        } catch (Exception e) {
            handleFailure(event, e);
        }
    }
    
    private void handleFailure(UserRegisteredEvent event, Exception e) {
        // 1. 记录失败信息
        logFailure(event, e);
        
        // 2. 根据异常类型决定处理策略
        if (isRetryableException(e)) {
            // 可重试异常,加入重试队列
            retryService.addToRetryQueue(event, e);
        } else {
            // 不可重试异常,发送告警
            alertService.sendAlert("事件处理失败", event, e);
        }
    }
    
    @Scheduled(fixedDelay = 60000) // 每分钟重试一次
    public void retryFailedEvents() {
        List<RetryTask> tasks = retryService.getRetryTasks();
        for (RetryTask task : tasks) {
            try {
                retryEventProcessing(task);
                retryService.markAsSuccess(task);
            } catch (Exception e) {
                retryService.incrementRetryCount(task);
                if (task.getRetryCount() >= MAX_RETRY_COUNT) {
                    alertService.sendAlert("重试次数超限", task, e);
                    retryService.markAsFailed(task);
                }
            }
        }
    }
}

2.6 失败处理总结

监听器类型 异常影响 事务影响 建议用途 失败处理策略
同步监听器 传播到发布者,中断流程 导致事务回滚 关键业务逻辑 谨慎处理异常,必要时抛出
异步监听器 不影响主流程 不影响主事务 非关键业务逻辑 配置异常处理器,实现重试机制
事务监听器 不影响已提交事务 主事务已提交 数据一致性处理 实现补偿机制,确保最终一致性

核心原则:

  • 🔴 关键流程用同步:必须成功的业务逻辑,失败时整体回滚
  • 🟡 非关键流程用异步:可以容忍失败的操作,实现优雅降级
  • 🔵 数据一致性用事务监听器:确保在事务确定后执行,实现最终一致性
  • 完善的异常处理和补偿机制:确保系统的健壮性和可恢复性

3. 项目结构

src/main/java/com/example/
├── EventDemoApplication.java          # 主应用类
├── config/                           # 配置类
│   ├── AsyncConfig.java             # 异步配置
│   └── EventConfig.java             # 事件配置
├── controller/                       # 控制器
│   └── UserController.java         # 用户控制器
├── entity/                          # 实体类
│   └── User.java                    # 用户实体
├── event/                           # 事件类
│   └── UserRegisteredEvent.java    # 用户注册事件
├── listener/                        # 事件监听器
│   ├── UserEventHandler.java       # @EventListener监听器
│   ├── AuditLogListener.java       # ApplicationListener监听器
│   └── TransactionalUserEventListener.java # 事务监听器
└── service/                         # 服务类
    ├── UserService.java            # 用户服务(事件发布者)
    ├── EmailService.java           # 邮件服务
    ├── NotificationService.java    # 通知服务
    ├── AuditService.java           # 审计服务
    ├── CacheService.java           # 缓存服务
    └── SearchIndexService.java     # 搜索索引服务

4. 核心代码实现

3.1 事件类定义

点击展开:UserRegisteredEvent.java - 用户注册事件
package com.example.event;

import org.springframework.context.ApplicationEvent;
import java.time.LocalDateTime;

/**
 * 用户注册事件
 */
public class UserRegisteredEvent extends ApplicationEvent {
    private final User user;
    private final LocalDateTime registeredTime;
    private final String registrationSource;
    public UserRegisteredEvent(Object source, User user, String registrationSource) {
        super(source);
        this.user = user;
        this.registeredTime = LocalDateTime.now();
        this.registrationSource = registrationSource;
    }
    // Getters
    public User getUser() {
        return user;
    }
    public LocalDateTime getRegisteredTime() {
        return registeredTime;
    }
    public String getRegistrationSource() {
        return registrationSource;
    }
    @Override
    public String toString() {
        return "UserRegisteredEvent{" +
                "user=" + user +
                ", registeredTime=" + registeredTime +
                ", registrationSource='" + registrationSource + '\'' +
                '}';
    }
}
点击展开:User.java - 用户实体类
package com.example.entity;

/**
 * 用户实体
 */
public class User {
    private Long id;
    private String username;
    private String email;
    private boolean vip;
    private String phone;
    // 构造函数
    public User() {}
    public User(Long id, String username, String email, boolean vip, String phone) {
        this.id = id;
        this.username = username;
        this.email = email;
        this.vip = vip;
        this.phone = phone;
    }
    // Getters and Setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    public boolean isVip() { return vip; }
    public void setVip(boolean vip) { this.vip = vip; }
    public String getPhone() { return phone; }
    public void setPhone(String phone) { this.phone = phone; }
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", email='" + email + '\'' +
                ", vip=" + vip +
                ", phone='" + phone + '\'' +
                '}';
    }
}

3.2 事件发布者

点击展开:UserService.java - 用户服务(事件发布者)
package com.example.service;

import com.example.entity.User;
import com.example.event.UserRegisteredEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 用户服务 - 事件发布者
 */
@Service
public class UserService {
    private static final Logger logger = LoggerFactory.getLogger(UserService.class);
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    /**
     * 用户注册方法
     */
    @Transactional
    public User registerUser(String username, String email, String phone, boolean isVip) {
        logger.info("开始注册用户: username={}, email={}", username, email);
        try {
            // 1. 创建用户对象
            User user = new User();
            user.setId(System.currentTimeMillis()); // 简单的ID生成
            user.setUsername(username);
            user.setEmail(email);
            user.setPhone(phone);
            user.setVip(isVip);
            // 2. 保存用户到数据库(模拟)
            saveUserToDatabase(user);
            // 3. 发布用户注册事件
            UserRegisteredEvent event = new UserRegisteredEvent(this, user, "WEB");
            logger.info("发布用户注册事件: {}", event);
            eventPublisher.publishEvent(event);
            logger.info("用户注册成功: {}", user);
            return user;
        } catch (Exception e) {
            logger.error("用户注册失败", e);
            throw new RuntimeException("用户注册失败", e);
        }
    }
    /**
     * 批量注册用户
     */
    @Transactional
    public void batchRegisterUsers(java.util.List<User> users) {
        logger.info("开始批量注册用户,数量: {}", users.size());
        for (User user : users) {
            // 保存用户
            saveUserToDatabase(user);
            // 发布事件
            UserRegisteredEvent event = new UserRegisteredEvent(this, user, "BATCH");
            eventPublisher.publishEvent(event);
        }
        logger.info("批量注册用户完成");
    }
    /**
     * 模拟保存用户到数据库
     */
    private void saveUserToDatabase(User user) {
        // 模拟数据库操作
        logger.debug("保存用户到数据库: {}", user);
        // 模拟可能的数据库异常
        if (user.getUsername().equals("error")) {
            throw new RuntimeException("数据库保存失败");
        }
    }
}

3.3 事件监听器

点击展开:UserEventHandler.java - @EventListener注解监听器
package com.example.listener;

import com.example.event.UserRegisteredEvent;
import com.example.service.EmailService;
import com.example.service.NotificationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

/**
 * 用户事件处理器 - 使用@EventListener注解
 */
@Component
public class UserEventHandler {
    private static final Logger logger = LoggerFactory.getLogger(UserEventHandler.class);
    @Autowired
    private EmailService emailService;
    @Autowired
    private NotificationService notificationService;
    /**
     * 处理用户注册事件 - 发送欢迎邮件
     */
    @EventListener
    @Order(1) // 优先级最高
    public void handleUserRegistered(UserRegisteredEvent event) {
        logger.info("处理用户注册事件 - 发送欢迎邮件: {}", event.getUser().getEmail());
        try {
            // 发送欢迎邮件
            emailService.sendWelcomeEmail(event.getUser());
            logger.info("欢迎邮件发送成功: {}", event.getUser().getEmail());
        } catch (Exception e) {
            logger.error("发送欢迎邮件失败: " + event.getUser().getEmail(), e);
            // 注意:同步监听器的异常会传播到事件发布者
        }
    }
    /**
     * 处理VIP用户注册 - 条件监听器
     */
    @EventListener(condition = "#event.user.vip == true")
    @Order(2)
    public void handleVipUserRegistered(UserRegisteredEvent event) {
        logger.info("处理VIP用户注册事件: {}", event.getUser().getUsername());
        try {
            // VIP用户特殊处理
            notificationService.sendVipWelcomeMessage(event.getUser());
            // 赠送VIP礼品
            giveVipGifts(event.getUser());
            logger.info("VIP用户注册处理完成: {}", event.getUser().getUsername());
        } catch (Exception e) {
            logger.error("VIP用户注册处理失败: " + event.getUser().getUsername(), e);
        }
    }
    /**
     * 异步处理用户注册 - 统计分析
     */
    @EventListener
    @Async
    @Order(3)
    public void handleUserRegisteredAsync(UserRegisteredEvent event) {
        logger.info("异步处理用户注册事件 - 统计分析: {}", event.getUser().getId());
        try {
            // 模拟耗时的统计分析操作
            Thread.sleep(2000);
            // 更新用户统计
            updateUserStatistics(event.getUser());
            // 推荐系统处理
            processRecommendations(event.getUser());
            logger.info("用户注册统计分析完成: {}", event.getUser().getId());
        } catch (Exception e) {
            logger.error("用户注册统计分析失败: " + event.getUser().getId(), e);
            // 异步监听器的异常不会传播到事件发布者
        }
    }
    /**
     * 处理来自特定来源的注册
     */
    @EventListener(condition = "#event.registrationSource == 'BATCH'")
    public void handleBatchRegistration(UserRegisteredEvent event) {
        logger.info("处理批量注册事件: {}", event.getUser().getUsername());
        // 批量注册的特殊处理逻辑
        // 例如:不发送单独的欢迎邮件,而是汇总发送
    }
    private void giveVipGifts(com.example.entity.User user) {
        logger.info("为VIP用户赠送礼品: {}", user.getUsername());
        // 实现VIP礼品逻辑
    }
    private void updateUserStatistics(com.example.entity.User user) {
        logger.info("更新用户统计信息: {}", user.getId());
        // 实现统计更新逻辑
    }
    private void processRecommendations(com.example.entity.User user) {
        logger.info("处理用户推荐: {}", user.getId());
        // 实现推荐系统逻辑
    }
}
点击展开:AuditLogListener.java - ApplicationListener接口监听器
package com.example.listener;

import com.example.event.UserRegisteredEvent;
import com.example.service.AuditService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

/**
 * 审计日志监听器 - 实现ApplicationListener接口
 */
@Component
public class AuditLogListener implements ApplicationListener<UserRegisteredEvent> {
    private static final Logger logger = LoggerFactory.getLogger(AuditLogListener.class);
    @Autowired
    private AuditService auditService;
    @Override
    public void onApplicationEvent(UserRegisteredEvent event) {
        logger.info("记录用户注册审计日志: {}", event.getUser().getId());
        try {
            // 记录审计日志
            auditService.logUserRegistration(
                event.getUser().getId(),
                event.getUser().getUsername(),
                event.getRegisteredTime(),
                event.getRegistrationSource()
            );
            logger.info("用户注册审计日志记录成功: {}", event.getUser().getId());
        } catch (Exception e) {
            logger.error("记录用户注册审计日志失败: " + event.getUser().getId(), e);
        }
    }
}
点击展开:TransactionalUserEventListener.java - 事务监听器
package com.example.listener;

import com.example.event.UserRegisteredEvent;
import com.example.service.CacheService;
import com.example.service.SearchIndexService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;

/**
 * 事务监听器 - 确保在事务提交后执行
 */
@Component
public class TransactionalUserEventListener {
    private static final Logger logger = LoggerFactory.getLogger(TransactionalUserEventListener.class);
    @Autowired
    private CacheService cacheService;
    @Autowired
    private SearchIndexService searchIndexService;
    /**
     * 事务提交后处理 - 更新缓存
     */
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handleAfterCommit(UserRegisteredEvent event) {
        logger.info("事务提交后处理用户注册事件: {}", event.getUser().getId());
        try {
            // 更新缓存
            cacheService.updateUserCache(event.getUser());
            // 更新搜索索引
            searchIndexService.indexUser(event.getUser());
            logger.info("事务提交后处理完成: {}", event.getUser().getId());
        } catch (Exception e) {
            logger.error("事务提交后处理失败: " + event.getUser().getId(), e);
        }
    }
    /**
     * 事务回滚后处理
     */
    @TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
    public void handleAfterRollback(UserRegisteredEvent event) {
        logger.warn("用户注册事务回滚,清理相关数据: {}", event.getUser().getId());
        try {
            // 清理可能已经创建的相关数据
            cleanupUserData(event.getUser());
        } catch (Exception e) {
            logger.error("事务回滚后清理失败: " + event.getUser().getId(), e);
        }
    }
    /**
     * 事务完成后处理(无论提交还是回滚)
     */
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION)
    public void handleAfterCompletion(UserRegisteredEvent event) {
        logger.info("用户注册事务完成: {}", event.getUser().getId());
        // 记录事务完成日志
        // 清理临时资源等
    }
    private void cleanupUserData(com.example.entity.User user) {
        logger.info("清理用户相关数据: {}", user.getId());
        // 实现数据清理逻辑
    }
}

3.4 支持服务类

点击展开:EmailService.java - 邮件服务
package com.example.service;

import com.example.entity.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

/**
 * 邮件服务
 */
@Service
public class EmailService {
    private static final Logger logger = LoggerFactory.getLogger(EmailService.class);
    /**
     * 发送欢迎邮件
     */
    public void sendWelcomeEmail(User user) {
        logger.info("发送欢迎邮件给用户: {}", user.getEmail());
        // 模拟邮件发送
        try {
            Thread.sleep(100); // 模拟网络延迟
            String emailContent = buildWelcomeEmailContent(user);
            // 实际的邮件发送逻辑
            sendEmail(user.getEmail(), "欢迎注册", emailContent);
            logger.info("欢迎邮件发送成功: {}", user.getEmail());
        } catch (Exception e) {
            logger.error("发送欢迎邮件失败: " + user.getEmail(), e);
            throw new RuntimeException("邮件发送失败", e);
        }
    }
    private String buildWelcomeEmailContent(User user) {
        return String.format(
            "亲爱的 %s,\n\n欢迎注册我们的服务!\n\n您的用户ID是:%d\n\n祝您使用愉快!",
            user.getUsername(), user.getId()
        );
    }
    private void sendEmail(String to, String subject, String content) {
        // 实际的邮件发送实现
        logger.debug("发送邮件 - 收件人: {}, 主题: {}", to, subject);
    }
}
点击展开:NotificationService.java - 通知服务
package com.example.service;

import com.example.entity.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

/**
 * 通知服务
 */
@Service
public class NotificationService {
    private static final Logger logger = LoggerFactory.getLogger(NotificationService.class);
    /**
     * 发送VIP欢迎消息
     */
    public void sendVipWelcomeMessage(User user) {
        logger.info("发送VIP欢迎消息给用户: {}", user.getUsername());
        try {
            // 发送短信通知
            sendSmsNotification(user.getPhone(), buildVipWelcomeMessage(user));
            // 发送App推送
            sendAppPushNotification(user.getId(), "VIP欢迎", "恭喜您成为VIP用户!");
            logger.info("VIP欢迎消息发送成功: {}", user.getUsername());
        } catch (Exception e) {
            logger.error("发送VIP欢迎消息失败: " + user.getUsername(), e);
        }
    }
    private String buildVipWelcomeMessage(User user) {
        return String.format("尊敬的VIP用户 %s,欢迎您!享受专属VIP服务。", user.getUsername());
    }
    private void sendSmsNotification(String phone, String message) {
        logger.debug("发送短信通知 - 手机号: {}, 内容: {}", phone, message);
        // 实际的短信发送实现
    }
    private void sendAppPushNotification(Long userId, String title, String content) {
        logger.debug("发送App推送 - 用户ID: {}, 标题: {}", userId, title);
        // 实际的App推送实现
    }
}
点击展开:AuditService.java - 审计服务
package com.example.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;

/**
 * 审计服务
 */
@Service
public class AuditService {
    private static final Logger logger = LoggerFactory.getLogger(AuditService.class);
    /**
     * 记录用户注册审计日志
     */
    public void logUserRegistration(Long userId, String username, 
                                  LocalDateTime registeredTime, String source) {
        logger.info("记录用户注册审计日志 - 用户ID: {}, 用户名: {}, 注册时间: {}, 来源: {}", 
                   userId, username, registeredTime, source);
        try {
            // 保存到审计日志表
            saveAuditLog("USER_REGISTRATION", userId, username, registeredTime, source);
            logger.debug("用户注册审计日志保存成功");
        } catch (Exception e) {
            logger.error("保存用户注册审计日志失败", e);
        }
    }
    
    private void saveAuditLog(String action, Long userId, String username, 
                            LocalDateTime timestamp, String source) {
        // 实际的审计日志保存实现
        logger.debug("保存审计日志 - 操作: {}, 用户: {}", action, username);
    }
}
点击展开:CacheService.java - 缓存服务
package com.example.service;

import com.example.entity.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

/**
 * 缓存服务
 */
@Service
public class CacheService {
    private static final Logger logger = LoggerFactory.getLogger(CacheService.class);
    /**
     * 更新用户缓存
     */
    public void updateUserCache(User user) {
        logger.info("更新用户缓存: {}", user.getId());
        try {
            // 更新Redis缓存
            updateRedisCache("user:" + user.getId(), user);
            // 更新本地缓存
            updateLocalCache(user);
            logger.debug("用户缓存更新成功: {}", user.getId());
        } catch (Exception e) {
            logger.error("更新用户缓存失败: " + user.getId(), e);
        }
    }
    private void updateRedisCache(String key, User user) {
        logger.debug("更新Redis缓存 - Key: {}", key);
        // 实际的Redis缓存更新实现
    }
    private void updateLocalCache(User user) {
        logger.debug("更新本地缓存 - 用户: {}", user.getId());
        // 实际的本地缓存更新实现
    }
}
点击展开:SearchIndexService.java - 搜索索引服务
package com.example.service;

import com.example.entity.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

/**
 * 搜索索引服务
 */
@Service
public class SearchIndexService {
    private static final Logger logger = LoggerFactory.getLogger(SearchIndexService.class);
    /**
     * 为用户建立搜索索引
     */
    public void indexUser(User user) {
        logger.info("为用户建立搜索索引: {}", user.getId());
        try {
            // 构建索引文档
            String indexDocument = buildUserIndexDocument(user);
            // 提交到Elasticsearch
            submitToElasticsearch("users", user.getId().toString(), indexDocument);
            logger.debug("用户搜索索引建立成功: {}", user.getId());
        } catch (Exception e) {
            logger.error("建立用户搜索索引失败: " + user.getId(), e);
        }
    }
    private String buildUserIndexDocument(User user) {
        // 构建Elasticsearch文档
        return String.format(
            "{\"id\":%d,\"username\":\"%s\",\"email\":\"%s\",\"vip\":%b}",
            user.getId(), user.getUsername(), user.getEmail(), user.isVip()
        );
    }
    private void submitToElasticsearch(String index, String id, String document) {
        logger.debug("提交到Elasticsearch - 索引: {}, ID: {}", index, id);
        // 实际的Elasticsearch提交实现
    }
}

3.5 配置类

点击展开:AsyncConfig.java - 异步配置
package com.example.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

/**
 * 异步配置
 */
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
    private static final Logger logger = LoggerFactory.getLogger(AsyncConfig.class);
    @Override
    @Bean(name = "taskExecutor")
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("EventAsync-");
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        executor.initialize();
        logger.info("异步任务执行器配置完成");
        return executor;
    }
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return (throwable, method, objects) -> {
            logger.error("异步方法执行异常 - 方法: {}, 参数: {}", 
                        method.getName(), objects, throwable);
        };
    }
}
点击展开:EventConfig.java - 事件配置
package com.example.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.core.task.SimpleAsyncTaskExecutor;

/**
 * 事件配置
 */
@Configuration
public class EventConfig {
    private static final Logger logger = LoggerFactory.getLogger(EventConfig.class);
    /**
     * 自定义事件多播器
     */
    @Bean(name = "applicationEventMulticaster")
    public ApplicationEventMulticaster simpleApplicationEventMulticaster() {
        SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();
        // 设置异步任务执行器
        eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
        // 设置错误处理器
        eventMulticaster.setErrorHandler(throwable -> {
            logger.error("事件处理异常", throwable);
        });
        logger.info("自定义事件多播器配置完成");
        return eventMulticaster;
    }
}

3.6 控制器和主应用类

点击展开:UserController.java - 用户控制器
package com.example.controller;

import com.example.entity.User;
import com.example.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Arrays;
import java.util.List;

/**
 * 用户控制器 - 测试事件处理
 */
@RestController
@RequestMapping("/api/users")
public class UserController {
    private static final Logger logger = LoggerFactory.getLogger(UserController.class);
    @Autowired
    private UserService userService;
    /**
     * 注册单个用户
     */
    @PostMapping("/register")
    public ResponseEntity<User> registerUser(@RequestBody UserRegistrationRequest request) {
        logger.info("接收用户注册请求: {}", request);
        try {
            User user = userService.registerUser(
                request.getUsername(),
                request.getEmail(),
                request.getPhone(),
                request.isVip()
            );
            return ResponseEntity.ok(user);
        } catch (Exception e) {
            logger.error("用户注册失败", e);
            return ResponseEntity.badRequest().build();
        }
    }
    /**
     * 批量注册用户
     */
    @PostMapping("/batch-register")
    public ResponseEntity<String> batchRegisterUsers() {
        logger.info("开始批量注册用户测试");
        try {
            List<User> users = Arrays.asList(
                new User(null, "user1", "user1@example.com", false, "13800000001"),
                new User(null, "user2", "user2@example.com", true, "13800000002"),
                new User(null, "user3", "user3@example.com", false, "13800000003")
            );
            userService.batchRegisterUsers(users);
            return ResponseEntity.ok("批量注册成功");
        } catch (Exception e) {
            logger.error("批量注册失败", e);
            return ResponseEntity.badRequest().body("批量注册失败");
        }
    }
    /**
     * 测试异常情况
     */
    @PostMapping("/register-error")
    public ResponseEntity<String> registerErrorUser() {
        logger.info("测试异常用户注册");
        try {
            userService.registerUser("error", "error@example.com", "13800000000", false);
            return ResponseEntity.ok("注册成功");
        } catch (Exception e) {
            logger.error("预期的注册异常", e);
            return ResponseEntity.badRequest().body("注册失败: " + e.getMessage());
        }
    }
    /**
     * 用户注册请求DTO
     */
    public static class UserRegistrationRequest {
        private String username;
        private String email;
        private String phone;
        private boolean vip;
        // Getters and Setters
        public String getUsername() { return username; }
        public void setUsername(String username) { this.username = username; }
        public String getEmail() { return email; }
        public void setEmail(String email) { this.email = email; }
        public String getPhone() { return phone; }
        public void setPhone(String phone) { this.phone = phone; }
        public boolean isVip() { return vip; }
        public void setVip(boolean vip) { this.vip = vip; }
        @Override
        public String toString() {
            return "UserRegistrationRequest{" +
                    "username='" + username + '\'' +
                    ", email='" + email + '\'' +
                    ", phone='" + phone + '\'' +
                    ", vip=" + vip +
                    '}';
        }
    }
}
点击展开:EventDemoApplication.java - 主应用类
package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;

/**
 * Spring Boot 事件处理示例应用
 */
@SpringBootApplication
@EnableTransactionManagement
public class EventDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(EventDemoApplication.class, args);
        System.out.println("Spring Boot 事件处理示例应用启动成功!");
        System.out.println("测试URL:");
        System.out.println("POST http://localhost:8080/api/users/register");
        System.out.println("POST http://localhost:8080/api/users/batch-register");
        System.out.println("POST http://localhost:8080/api/users/register-error");
    }
}

3.7 配置文件和测试

点击展开:application.yml - 配置文件
server:
  port: 8080

spring:
  application:
    name: spring-boot-event-demo
logging:
  level:
    com.example: DEBUG
    org.springframework.context.event: DEBUG
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"

# 自定义配置
app:
  event:
    async:
      core-pool-size: 5
      max-pool-size: 10
      queue-capacity: 100
点击展开:EventProcessingTest.java - 测试用例
package com.example;

import com.example.entity.User;
import com.example.event.UserRegisteredEvent;
import com.example.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

import static org.junit.jupiter.api.Assertions.*;

/**
 * 事件处理测试
 */
@SpringBootTest
@SpringJUnitConfig
public class EventProcessingTest {
    @Autowired
    private UserService userService;
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    @Test
    public void testUserRegistration() {
        // 测试用户注册和事件处理
        User user = userService.registerUser("testuser", "test@example.com", "13800000000", false);
        assertNotNull(user);
        assertEquals("testuser", user.getUsername());
        assertEquals("test@example.com", user.getEmail());
    }
    @Test
    public void testVipUserRegistration() {
        // 测试VIP用户注册
        User vipUser = userService.registerUser("vipuser", "vip@example.com", "13800000001", true);
        assertNotNull(vipUser);
        assertTrue(vipUser.isVip());
    }
    @Test
    public void testDirectEventPublishing() {
        // 直接发布事件测试
        User user = new User(999L, "directuser", "direct@example.com", false, "13800000002");
        UserRegisteredEvent event = new UserRegisteredEvent(this, user, "TEST");
        // 发布事件
        eventPublisher.publishEvent(event);
        // 验证事件已发布(通过日志观察)
        assertNotNull(event);
    }
}

4. 运行和测试

4.1 启动应用

# 编译和运行
mvn clean compile
mvn spring-boot:run

# 或者打包运行
mvn clean package
java -jar target/spring-boot-event-demo.jar

4.2 API测试

点击展开:API测试命令
# 注册普通用户
curl -X POST http://localhost:8080/api/users/register \
  -H "Content-Type: application/json" \
  -d '{
    "username": "john",
    "email": "john@example.com",
    "phone": "13800000001",
    "vip": false
  }'

# 注册VIP用户
curl -X POST http://localhost:8080/api/users/register \
  -H "Content-Type: application/json" \
  -d '{
    "username": "vipjohn",
    "email": "vipjohn@example.com",
    "phone": "13800000002",
    "vip": true
  }'

# 批量注册测试
curl -X POST http://localhost:8080/api/users/batch-register

# 异常情况测试
curl -X POST http://localhost:8080/api/users/register-error

4.3 日志观察

运行应用后,观察控制台日志输出,可以看到:

  • 事件发布日志:显示事件何时被发布
  • 监听器处理日志:各个监听器的执行顺序和结果
  • 同步vs异步:同步监听器立即执行,异步监听器在后台执行
  • 事务监听器:在事务提交后才执行
  • 异常处理:同步监听器异常会影响主流程,异步不会

5. 核心特性总结

5.1 事件处理类型

监听器类型 特点 使用场景
@EventListener 简单易用,支持条件监听 大部分业务场景
ApplicationListener 传统方式,类型安全 需要类型安全的场景
@TransactionalEventListener 事务感知,确保数据一致性 需要事务保证的场景
@Async + @EventListener 异步处理,不阻塞主流程 耗时操作,如统计分析

5.2 设计模式应用

  • 观察者模式:事件发布-订阅机制
  • 模板方法模式:ApplicationListener接口
  • 策略模式:不同类型的事件处理策略
  • 责任链模式:多个监听器按顺序处理

5.3 最佳实践

  1. 事件设计:事件应该是不可变对象,包含足够的上下文信息
  2. 监听器设计:保持监听器方法简单,耗时操作使用@Async
  3. 异常处理:同步监听器要处理好异常,异步监听器配置ErrorHandler
  4. 性能优化:避免在监听器中执行阻塞操作,使用条件监听减少无效调用

这个完整的示例展示了Spring Boot事件处理机制的所有核心概念和最佳实践,可以作为实际项目中事件驱动架构设计的参考模板。

posted @ 2025-06-23 18:49  tokirin994  阅读(118)  评论(0)    收藏  举报