Spring事件监听器在电商订单系统中的应用

📦 Spring事件监听器完整Demo

以下是一个完整的Spring Boot项目示例,演示@EventListener的多种用法。这个Demo模拟了一个电商订单系统,通过事件机制解耦订单创建后的各种处理逻辑。

项目结构

复制代码
src/main/java/com/example/eventdemo/
├── EventDemoApplication.java      # 启动类
├── config/
│   └── AsyncConfig.java           # 异步配置
├── event/
│   ├── OrderCreatedEvent.java     # 订单创建事件
│   ├── OrderPaidEvent.java        # 订单支付事件
│   └── OrderCancelledEvent.java   # 订单取消事件
├── model/
│   └── Order.java                 # 订单实体
├── service/
│   ├── OrderService.java          # 订单服务
│   ├── EmailService.java          # 邮件服务
│   ├── SmsService.java            # 短信服务
│   ├── InventoryService.java      # 库存服务
│   └── AuditService.java          # 审计服务
└── controller/
    └── OrderController.java       # 订单控制器

1. 启动类和配置

java 复制代码
// EventDemoApplication.java
@SpringBootApplication
@EnableAsync  // 启用异步支持
public class EventDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(EventDemoApplication.class, args);
    }
}

// AsyncConfig.java - 线程池配置
@Configuration
public class AsyncConfig {
    @Bean("taskExecutor")
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(25);
        executor.setThreadNamePrefix("EventListener-");
        executor.initialize();
        return executor;
    }
}

2. 事件定义

java 复制代码
// OrderCreatedEvent.java - 订单创建事件
public class OrderCreatedEvent {
    private Long orderId;
    private String customerName;
    private String customerEmail;
    private String customerPhone;
    private BigDecimal amount;
    private LocalDateTime createTime;
    
    // 构造器、getter、setter
    public OrderCreatedEvent(Long orderId, String customerName, 
                           String customerEmail, String customerPhone, 
                           BigDecimal amount) {
        this.orderId = orderId;
        this.customerName = customerName;
        this.customerEmail = customerEmail;
        this.customerPhone = customerPhone;
        this.amount = amount;
        this.createTime = LocalDateTime.now();
    }
    
    // getter 方法...
}
java 复制代码
// OrderPaidEvent.java - 订单支付事件
public class OrderPaidEvent {
    private Long orderId;
    private String paymentMethod;
    private LocalDateTime paidTime;
    
    public OrderPaidEvent(Long orderId, String paymentMethod) {
        this.orderId = orderId;
        this.paymentMethod = paymentMethod;
        this.paidTime = LocalDateTime.now();
    }
    // getter...
}

3. 服务类

java 复制代码
// OrderService.java - 订单服务(事件发布者)
@Service
@Slf4j
public class OrderService {
    
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    
    public Order createOrder(String customerName, String email, 
                           String phone, BigDecimal amount) {
        log.info("1. 开始创建订单...");
        
        // 模拟创建订单(实际应保存到数据库)
        Order order = new Order();
        order.setId(System.currentTimeMillis()); // 模拟ID
        order.setCustomerName(customerName);
        order.setCustomerEmail(email);
        order.setCustomerPhone(phone);
        order.setAmount(amount);
        order.setStatus("CREATED");
        order.setCreateTime(LocalDateTime.now());
        
        log.info("2. 订单创建成功,订单号: {}", order.getId());
        
        // 发布订单创建事件
        eventPublisher.publishEvent(
            new OrderCreatedEvent(order.getId(), customerName, 
                                email, phone, amount)
        );
        
        log.info("3. 订单创建事件已发布,继续处理其他业务...");
        
        return order;
    }
    
    public void payOrder(Long orderId, String paymentMethod) {
        log.info("订单 {} 支付成功,支付方式: {}", orderId, paymentMethod);
        
        // 发布订单支付事件
        eventPublisher.publishEvent(
            new OrderPaidEvent(orderId, paymentMethod)
        );
    }
}

4. 监听器实现(核心Demo)

java 复制代码
// EmailListener.java - 邮件通知监听器
@Component
@Slf4j
public class EmailListener {
    
    @Autowired
    private EmailService emailService;
    
    /**
     * 监听订单创建事件,发送确认邮件
     * 使用@Async实现异步发送,不阻塞主流程
     */
    @EventListener
    @Async("taskExecutor")
    public void handleOrderCreated(OrderCreatedEvent event) {
        log.info("[邮件监听器] 开始处理订单创建事件,订单号: {}", event.getOrderId());
        
        try {
            // 模拟邮件发送耗时
            Thread.sleep(2000);
            
            emailService.sendOrderConfirmation(
                event.getCustomerEmail(),
                event.getOrderId(),
                event.getAmount()
            );
            
            log.info("[邮件监听器] 订单确认邮件已发送至: {}", event.getCustomerEmail());
        } catch (InterruptedException e) {
            log.error("[邮件监听器] 发送邮件失败", e);
        }
    }
    
    /**
     * 监听订单支付事件,发送支付成功邮件
     * 条件:只有金额大于100的订单才发送详细邮件
     */
    @EventListener(condition = "#event.orderId != null")
    @Async
    public void handleOrderPaid(OrderPaidEvent event) {
        log.info("[邮件监听器] 处理订单支付事件,订单号: {}", event.getOrderId());
        
        // 实际项目中,这里会根据orderId查询订单详情
        emailService.sendPaymentSuccess(event.getOrderId());
    }
}
java 复制代码
// SmsListener.java - 短信通知监听器
@Component
@Slf4j
public class SmsListener {
    
    @Autowired
    private SmsService smsService;
    
    /**
     * 监听订单创建事件,发送短信通知
     * 使用@Order指定执行顺序(数值越小优先级越高)
     */
    @EventListener
    @Order(1)  // 优先于其他监听器执行
    public void sendOrderSms(OrderCreatedEvent event) {
        log.info("[短信监听器] 发送订单创建短信,订单号: {}", event.getOrderId());
        
        smsService.sendOrderCreatedSms(
            event.getCustomerPhone(),
            event.getOrderId()
        );
    }
    
    /**
     * 监听订单支付事件,发送支付成功短信
     * 演示泛型事件监听
     */
    @EventListener
    public void sendPaymentSms(Object event) {
        if (event instanceof OrderPaidEvent) {
            OrderPaidEvent paidEvent = (OrderPaidEvent) event;
            log.info("[短信监听器] 发送支付成功短信,订单号: {}", paidEvent.getOrderId());
            smsService.sendPaymentSuccessSms(paidEvent.getOrderId());
        }
    }
}
java 复制代码
// InventoryListener.java - 库存监听器
@Component
@Slf4j
public class InventoryListener {
    
    @Autowired
    private InventoryService inventoryService;
    
    /**
     * 监听订单创建事件,扣减库存
     * 使用try-catch确保不影响其他监听器
     */
    @EventListener
    @Order(2)  // 在短信之后执行
    public void deductInventory(OrderCreatedEvent event) {
        log.info("[库存监听器] 开始扣减库存,订单号: {}", event.getOrderId());
        
        try {
            // 模拟库存扣减
            boolean success = inventoryService.deductStock(event.getOrderId());
            
            if (success) {
                log.info("[库存监听器] 库存扣减成功");
            } else {
                log.warn("[库存监听器] 库存不足,订单号: {}", event.getOrderId());
            }
        } catch (Exception e) {
            // 捕获异常,避免影响其他监听器
            log.error("[库存监听器] 扣减库存失败", e);
        }
    }
}
java 复制代码
// AuditListener.java - 审计监听器
@Component
@Slf4j
public class AuditListener {
    
    @Autowired
    private AuditService auditService;
    
    /**
     * 监听所有订单相关事件,记录审计日志
     * 演示一个方法监听多个事件
     */
    @EventListener
    public void auditOrderEvent(Object event) {
        if (event instanceof OrderCreatedEvent) {
            OrderCreatedEvent createdEvent = (OrderCreatedEvent) event;
            auditService.logOrderCreation(
                createdEvent.getOrderId(),
                createdEvent.getCustomerName(),
                createdEvent.getAmount()
            );
            log.info("[审计监听器] 记录订单创建审计日志");
        }
        else if (event instanceof OrderPaidEvent) {
            OrderPaidEvent paidEvent = (OrderPaidEvent) event;
            auditService.logOrderPayment(
                paidEvent.getOrderId(),
                paidEvent.getPaymentMethod()
            );
            log.info("[审计监听器] 记录订单支付审计日志");
        }
    }
    
    /**
     * 监听订单创建事件,只处理大额订单(金额 > 1000)
     * 使用SpEL表达式定义条件
     */
    @EventListener(condition = "#event.amount.compareTo(1000) > 0")
    public void handleLargeOrder(OrderCreatedEvent event) {
        log.warn("[审计监听器] 大额订单警告!订单号: {}, 金额: {}", 
                event.getOrderId(), event.getAmount());
        auditService.flagLargeOrder(event.getOrderId(), event.getAmount());
    }
}
java 复制代码
// StatisticsListener.java - 统计监听器
@Component
@Slf4j
public class StatisticsListener {
    
    private final AtomicInteger orderCount = new AtomicInteger(0);
    private final AtomicReference<BigDecimal> totalAmount = 
        new AtomicReference<>(BigDecimal.ZERO);
    
    /**
     * 监听订单创建事件,更新统计信息
     * 演示有返回值的事件监听(返回值会作为新事件发布)
     */
    @EventListener
    public OrderStatisticsEvent updateStatistics(OrderCreatedEvent event) {
        int count = orderCount.incrementAndGet();
        BigDecimal amount = totalAmount.updateAndGet(
            current -> current.add(event.getAmount())
        );
        
        log.info("[统计监听器] 订单统计更新: 总数={}, 总金额={}", count, amount);
        
        // 返回一个新的事件,会被自动发布
        return new OrderStatisticsEvent(count, amount);
    }
    
    /**
     * 监听统计事件(由上一個监听器产生)
     */
    @EventListener
    public void handleStatistics(OrderStatisticsEvent event) {
        log.info("[统计监听器] 收到统计事件: {}", event);
        
        // 这里可以执行一些周期性统计任务
        if (event.getOrderCount() % 10 == 0) {
            log.info("[统计监听器] 已处理 {} 个订单,生成统计报表", event.getOrderCount());
        }
    }
    
    // 内部统计事件类
    public static class OrderStatisticsEvent {
        private final int orderCount;
        private final BigDecimal totalAmount;
        
        public OrderStatisticsEvent(int orderCount, BigDecimal totalAmount) {
            this.orderCount = orderCount;
            this.totalAmount = totalAmount;
        }
        // getter...
    }
}

5. 辅助服务类(模拟实现)

java 复制代码
// EmailService.java
@Service
@Slf4j
public class EmailService {
    public void sendOrderConfirmation(String email, Long orderId, BigDecimal amount) {
        log.info("📧 发送订单确认邮件到: {}, 订单号: {}, 金额: {}", email, orderId, amount);
        // 实际发送邮件逻辑
    }
    
    public void sendPaymentSuccess(Long orderId) {
        log.info("📧 发送支付成功邮件,订单号: {}", orderId);
    }
}

// SmsService.java
@Service
@Slf4j
public class SmsService {
    public void sendOrderCreatedSms(String phone, Long orderId) {
        log.info("📱 发送订单创建短信到: {}, 订单号: {}", phone, orderId);
    }
    
    public void sendPaymentSuccessSms(Long orderId) {
        log.info("📱 发送支付成功短信,订单号: {}", orderId);
    }
}

// InventoryService.java
@Service
@Slf4j
public class InventoryService {
    public boolean deductStock(Long orderId) {
        log.info("📦 扣减库存,订单号: {}", orderId);
        // 模拟库存扣减,90%成功率
        return Math.random() > 0.1;
    }
}

// AuditService.java
@Service
@Slf4j
public class AuditService {
    public void logOrderCreation(Long orderId, String customer, BigDecimal amount) {
        log.info("📝 审计日志 - 订单创建: ID={}, 客户={}, 金额={}", 
                orderId, customer, amount);
    }
    
    public void logOrderPayment(Long orderId, String paymentMethod) {
        log.info("📝 审计日志 - 订单支付: ID={}, 支付方式={}", orderId, paymentMethod);
    }
    
    public void flagLargeOrder(Long orderId, BigDecimal amount) {
        log.info("⚠️  大额订单标记: ID={}, 金额={}", orderId, amount);
    }
}

6. 控制器(测试端点)

java 复制代码
// OrderController.java
@RestController
@RequestMapping("/orders")
@Slf4j
public class OrderController {
    
    @Autowired
    private OrderService orderService;
    
    @PostMapping("/create")
    public ResponseEntity<Map<String, Object>> createOrder(
            @RequestParam String customerName,
            @RequestParam String email,
            @RequestParam String phone,
            @RequestParam BigDecimal amount) {
        
        log.info("\n========== 收到创建订单请求 ==========");
        log.info("客户: {}, 金额: {}", customerName, amount);
        
        long startTime = System.currentTimeMillis();
        
        // 创建订单(会触发事件)
        Order order = orderService.createOrder(customerName, email, phone, amount);
        
        long endTime = System.currentTimeMillis();
        
        Map<String, Object> response = new HashMap<>();
        response.put("success", true);
        response.put("orderId", order.getId());
        response.put("message", "订单创建成功");
        response.put("processingTime", (endTime - startTime) + "ms");
        response.put("note", "邮件/短信等通知正在异步处理中...");
        
        log.info("订单创建完成,总处理时间: {}ms", (endTime - startTime));
        log.info("========== 请求处理完成 ==========\n");
        
        return ResponseEntity.ok(response);
    }
    
    @PostMapping("/{orderId}/pay")
    public ResponseEntity<String> payOrder(
            @PathVariable Long orderId,
            @RequestParam String paymentMethod) {
        
        orderService.payOrder(orderId, paymentMethod);
        
        return ResponseEntity.ok("支付成功");
    }
}

7. 模型类

java 复制代码
// Order.java
@Data
public class Order {
    private Long id;
    private String customerName;
    private String customerEmail;
    private String customerPhone;
    private BigDecimal amount;
    private String status;
    private LocalDateTime createTime;
}

8. 测试运行

方式一:使用cURL测试

bash 复制代码
# 创建订单
curl -X POST "http://localhost:8080/orders/create?customerName=张三&email=zhangsan@example.com&phone=13800138000&amount=1500.50"

# 订单支付
curl -X POST "http://localhost:8080/orders/123456/pay?paymentMethod=ALIPAY"

方式二:使用单元测试

java 复制代码
@SpringBootTest
@Slf4j
class OrderEventTests {
    
    @Autowired
    private OrderService orderService;
    
    @Test
    void testOrderCreatedEvent() {
        log.info("测试开始...");
        
        // 创建订单,会触发事件
        Order order = orderService.createOrder(
            "测试用户",
            "test@example.com",
            "13800138000",
            new BigDecimal("2999.99")
        );
        
        // 等待异步事件处理完成
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        log.info("测试完成");
    }
}

9. 预期输出效果

当你调用创建订单API时,控制台会输出:

复制代码
========== 收到创建订单请求 ==========
客户: 张三, 金额: 1500.50

1. 开始创建订单...
2. 订单创建成功,订单号: 1646389471234
3. 订单创建事件已发布,继续处理其他业务...

[短信监听器] 发送订单创建短信,订单号: 1646389471234
📱 发送订单创建短信到: 13800138000, 订单号: 1646389471234

[库存监听器] 开始扣减库存,订单号: 1646389471234
📦 扣减库存,订单号: 1646389471234
[库存监听器] 库存扣减成功

[邮件监听器] 开始处理订单创建事件,订单号: 1646389471234
📧 发送订单确认邮件到: zhangsan@example.com, 订单号: 1646389471234, 金额: 1500.50
[邮件监听器] 订单确认邮件已发送至: zhangsan@example.com

[审计监听器] 记录订单创建审计日志
📝 审计日志 - 订单创建: ID=1646389471234, 客户=张三, 金额=1500.50
[审计监听器] 大额订单警告!订单号: 1646389471234, 金额: 1500.50
⚠️  大额订单标记: ID=1646389471234, 金额=1500.50

[统计监听器] 订单统计更新: 总数=1, 总金额=1500.50
[统计监听器] 收到统计事件: OrderStatisticsEvent{orderCount=1, totalAmount=1500.50}

订单创建完成,总处理时间: 35ms
========== 请求处理完成 ==========

📌 Demo亮点总结

  1. 多种监听方式

    • 直接监听具体事件类型
    • 使用Object监听所有事件
    • 使用SpEL条件表达式过滤
  2. 完整的事件处理特性

    • @Async异步处理(邮件发送)
    • @Order执行顺序控制
    • 异常处理不影响其他监听器
    • 有返回值的事件链
  3. 实际业务场景

    • 订单创建后的多维度处理
    • 大额订单特殊处理
    • 审计日志记录
    • 统计信息更新
  4. 架构价值体现

    • 主流程(订单创建)响应时间仅35ms
    • 耗时操作(邮件发送2秒)异步处理
    • 新增处理逻辑只需添加监听器,无需修改订单服务

这个Demo完整展示了@EventListener在生产环境中的典型应用,体现了事件驱动架构在解耦业务、提升性能方面的优势。

相关推荐
自在极意功。1 小时前
手写Tomcat:深入理解Servlet容器工作原理
java·servlet·tomcat·socket
Boop_wu1 小时前
[Java EE] 字符流和字节流实例
java·开发语言·apache
shangjian0071 小时前
Python基础-闭包和装饰器
开发语言·python
Arva .1 小时前
讲一下 Spring 中用到的设计模式
java·spring·设计模式
三维空间1 小时前
如何在Python多进程中避免死锁问题?
python
冤大头编程之路1 小时前
Python并发编程实操教程:多线程/多进程/异步全解析
python
bbq粉刷匠1 小时前
Java-顺序表
java
dhdjjsjs2 小时前
Day30 Python Study
开发语言·前端·python
Eric.Lee20212 小时前
mujoco构建无物理约束的几何体运动
python·物理引擎·mujoco·物理模型仿真