优雅!Spring Boot 3.3 实现职责链模式,轻松应对电商订单流程

简介: 本文介绍如何使用 Spring Boot 3.3 实现职责链模式,优化电商订单处理流程。通过将订单处理的各个环节(如库存校验、优惠券核验、支付处理等)封装为独立的处理器,并通过职责链将这些处理器串联起来,实现了代码的解耦和灵活扩展。具体实现包括订单请求类 `OrderRequest`、抽象处理器类 `OrderHandler`、具体处理器实现(如 `OrderValidationHandler`、`VerifyCouponHandler` 等)、以及初始化职责链的配置类 `OrderChainConfig`。

优雅!Spring Boot 3.3 实现职责链模式,轻松应对电商订单流程

在电商系统中,订单的处理流程通常涉及多个步骤,每个步骤都可能有不同的业务逻辑。例如,当用户提交订单时,系统需要校验库存、验证优惠券、计算运费、处理支付、分配物流等。这些操作看似独立,但实际上具有一定的顺序依赖性。为了更好地管理这些业务逻辑,我们需要将这些流程模块化,并按需执行。

通常的做法是将所有逻辑写在一起,但这会导致代码冗长且难以维护。如果未来需要对某个步骤进行修改或者添加新的处理环节,代码变动的范围将会很大。为了避免这种情况,职责链模式提供了一个灵活、可扩展的解决方案。

什么是职责链模式?

职责链模式(Chain of Responsibility)是一种行为设计模式,它允许多个对象都有机会处理请求,直到其中一个对象处理成功为止。职责链模式使多个处理对象通过链式关系链接在一起,每个处理对象知道它的下一个处理对象,并且在完成自身处理后,将请求传递给下一个对象。

职责链模式的优点:

  1. 解耦请求发送者和接收者:请求发送者不需要知道是谁在处理请求,避免了系统中各个模块的强耦合。
  2. 灵活扩展:通过调整链条中的处理器顺序,或者增加新的处理器,可以灵活地扩展业务逻辑。
  3. 动态组合:职责链的处理器可以动态组合,可以根据不同的需求创建不同的链条。

适用场景:

  • 需要对请求进行一系列处理,并且请求处理者不明确或不固定时。
  • 多个对象可以处理同一个请求,具体由运行时动态决定哪个对象来处理。

职责链模式在电商订单流程中的应用

在电商系统中,职责链模式可以将订单处理过程中的各个环节(如库存校验、优惠券核验、支付处理等)封装为独立的处理器,并通过职责链将这些处理器串联起来。每个处理器独立处理其对应的任务,处理完成后将请求传递给下一个处理器,直到所有处理环节完成或者中断。

运行效果:

ab6720bf4b32e2ad2e50abbfacd7c30.png

若想获取项目完整代码以及其他文章的项目源码,且在代码编写时遇到问题需要咨询交流,欢迎加入下方的知识星球。

本文将深入探讨如何通过职责链模式来处理电商订单流程,并结合 Spring Boot 3.3 和前后端代码示例,展示如何实现这一模式。同时,前端使用 jQuery 调用后端 JSON 接口,并通过 Bootstrap 提示用户订单处理的结果。

POM 文件配置

项目中我们需要使用 Spring Boot 和 Thymeleaf 模板引擎,具体依赖配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.3.3</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>
  <groupId>com.icoderoad</groupId>
  <artifactId>order-chain</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>order-chain</name>
  <description>Demo project for Spring Boot</description>
  
  <properties>
    <java.version>17</java.version>
  </properties>
  <dependencies>
    <!-- Spring Boot 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Thymeleaf 模板引擎 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        
         <!-- Lombok 插件 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>

配置文件 application.yml

使用 @ConfigurationProperties 读取订单处理链的配置步骤:

order:
  chain:
    steps:
      - "orderValidationHandler"
      - "verifyCouponHandler"
      - "shippingFeeHandler"
      - "totalAmountHandler"
      - "processPaymentHandler"

订单处理职责链实现

为了优化 Handler 方法,可以结合订单的处理流程,对不同的 Handler 进行职责分工,比如验证订单信息、处理优惠券、计算运费和最终结算等步骤。我们可以使用职责链模式(Chain of Responsibility)将这些不同的处理逻辑独立封装在各自的 Handler 类中,并且每个 Handler负责处理其自身的逻辑,处理完后将处理流程交给下一个 Handler

我们将实现以下几个 Handler

  1. 订单验证处理器(OrderValidationHandler:负责验证订单的基本信息,比如商品是否存在、库存是否充足等。
  2. 优惠券处理器(CouponHandler:负责处理优惠券的校验和折扣计算。
  3. 运费处理器(ShippingFeeHandler:负责计算订单的运费。
  4. 总金额处理器(TotalAmountHandler:负责计算订单的总金额。
  5. 支付处理器( PaymentHandler`):负责订单支付功能。

每个 Handler 类都遵循职责链的接口,将逻辑封装在 Handler 中,最后调用下一个 Handler

订单请求类 OrderRequest.java

package com.icoderoad.orderchain.entity;
import java.math.BigDecimal;
import java.util.List;
import lombok.Data;
@Data
public class OrderRequest {
    // 商品列表
    private List<Product> productList;
    // 用户使用的优惠券
    private String couponCode;
    // 运费
    private BigDecimal shippingFee;
    // 订单总金额
    private BigDecimal totalAmount;
    
    public OrderRequest() {}
    // 构造方法
    public OrderRequest(List<Product> productList, String couponCode, BigDecimal shippingFee, BigDecimal totalAmount) {
        this.productList = productList;
        this.couponCode = couponCode;
        this.shippingFee = shippingFee;
        this.totalAmount = totalAmount;
    }
    // 计算订单总金额(含运费和扣除优惠)
    public BigDecimal calculateTotalAmount() {
        BigDecimal productTotal = productList.stream()
                .map(product -> product.getPrice().multiply(BigDecimal.valueOf(product.getQuantity())))
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        // 简单模拟优惠金额
        BigDecimal discount = (couponCode != null && !couponCode.isEmpty()) ? BigDecimal.valueOf(10) : BigDecimal.ZERO;
        return productTotal.add(shippingFee).subtract(discount);
    }
    @Data
    // 商品类
    public static class Product {
        private String productId;
        private String name;
        private int quantity;
        private BigDecimal price;
        // 构造方法
        public Product(String productId, String name, int quantity, BigDecimal price) {
            this.productId = productId;
            this.name = name;
            this.quantity = quantity;
            this.price = price;
        }
        
    }
}

抽象处理器类 OrderHandler.java

package com.icoderoad.orderchain.handler;
import com.icoderoad.orderchain.entity.OrderRequest;
public abstract class OrderHandler {
    protected OrderHandler nextHandler;
    // 设置下一个处理器
    public void setNextHandler(OrderHandler nextHandler) {
        this.nextHandler = nextHandler;
    }
    // 抽象方法,处理订单
    public abstract void handle(OrderRequest request);
}

具体处理器实现

库存校验处理器 OrderValidationHandler

package com.icoderoad.orderchain.handler;
import org.springframework.stereotype.Component;
import com.icoderoad.orderchain.entity.OrderRequest;
@Component
public class OrderValidationHandler extends OrderHandler {
    @Override
    public void handle(OrderRequest orderRequest) {
        // 验证商品列表是否为空
        if (orderRequest.getProductList() == null || orderRequest.getProductList().isEmpty()) {
            throw new IllegalArgumentException("订单中没有商品");
        }
        // 验证每个商品的库存(此处为模拟逻辑)
        for (OrderRequest.Product product : orderRequest.getProductList()) {
            if (product.getQuantity() <= 0) {
                throw new IllegalArgumentException("商品库存不足: " + product.getName());
            }
        }
        System.out.println("订单验证通过");
        // 调用下一个处理器
        if (nextHandler != null) {
            nextHandler.handle(orderRequest);
        }
    }
}

优惠券核验处理器 VerifyCouponHandler.java

package com.icoderoad.orderchain.handler;
import java.math.BigDecimal;
import org.springframework.stereotype.Component;
import com.icoderoad.orderchain.entity.OrderRequest;
@Component
public class VerifyCouponHandler extends OrderHandler {
    @Override
    public void handle(OrderRequest orderRequest) {
        String couponCode = orderRequest.getCouponCode();
        // 简单模拟优惠券验证逻辑
        if (couponCode != null && !couponCode.isEmpty()) {
            // 假设优惠券折扣金额为 10
            BigDecimal discount = new BigDecimal("10.00");
            System.out.println("使用优惠券: " + couponCode + ",折扣金额: " + discount);
        } else {
            System.out.println("未使用优惠券");
        }
        // 调用下一个处理器
        if (nextHandler != null) {
            nextHandler.handle(orderRequest);
        }
    }
}

运费处理器

package com.icoderoad.orderchain.handler;
import java.math.BigDecimal;
import org.springframework.stereotype.Component;
import com.icoderoad.orderchain.entity.OrderRequest;
@Component
public class ShippingFeeHandler extends OrderHandler {
    @Override
    public void handle(OrderRequest orderRequest) {
        // 简单模拟运费计算逻辑
        BigDecimal shippingFee = orderRequest.getShippingFee();
        System.out.println("运费: " + shippingFee);
        // 调用下一个处理器
        if (nextHandler != null) {
            nextHandler.handle(orderRequest);
        }
    }
}

总金额处理器

package com.icoderoad.orderchain.handler;
import java.math.BigDecimal;
import org.springframework.stereotype.Component;
import com.icoderoad.orderchain.entity.OrderRequest;
@Component
public class TotalAmountHandler extends OrderHandler {
    @Override
    public void handle(OrderRequest orderRequest) {
        // 计算订单总金额
        BigDecimal totalAmount = orderRequest.calculateTotalAmount();
        orderRequest.setTotalAmount(totalAmount);
        System.out.println("订单总金额: " + totalAmount);
        // 调用下一个处理器(如果有)
        if (nextHandler != null) {
            nextHandler.handle(orderRequest);
        }
    }
}

支付处理器 ProcessPaymentHandler.java

package com.icoderoad.orderchain.handler;
import org.springframework.stereotype.Component;
import com.icoderoad.orderchain.entity.OrderRequest;
@Component
public class ProcessPaymentHandler extends OrderHandler {
    @Override
    public void handle(OrderRequest request) {
        // 支付处理逻辑
        System.out.println("正在处理支付...");
        
        // 支付完成,职责链结束
    }
}

初始化职责链 OrderChainConfig.java

package com.icoderoad.orderchain.config;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.icoderoad.orderchain.handler.OrderHandler;
import com.icoderoad.orderchain.handler.OrderValidationHandler;
import com.icoderoad.orderchain.handler.ProcessPaymentHandler;
import com.icoderoad.orderchain.handler.ShippingFeeHandler;
import com.icoderoad.orderchain.handler.TotalAmountHandler;
import com.icoderoad.orderchain.handler.VerifyCouponHandler;
@Configuration
@ConfigurationProperties(prefix = "order.chain")
public class OrderChainConfig {
    private List<String> steps;
    // 将处理器的映射存储在一个集合中
    private final Map<String, OrderHandler> handlerMap = new HashMap<>();
    public OrderChainConfig(List<OrderHandler> handlers) {
        // 初始化处理器映射
        handlerMap.put("orderValidationHandler", handlers.stream().filter(h -> h instanceof OrderValidationHandler).findFirst().orElse(null));
        handlerMap.put("verifyCouponHandler", handlers.stream().filter(h -> h instanceof VerifyCouponHandler).findFirst().orElse(null));
        handlerMap.put("shippingFeeHandler", handlers.stream().filter(h -> h instanceof ShippingFeeHandler).findFirst().orElse(null));
        handlerMap.put("totalAmountHandler", handlers.stream().filter(h -> h instanceof TotalAmountHandler).findFirst().orElse(null));
        handlerMap.put("processPaymentHandler", handlers.stream().filter(h -> h instanceof ProcessPaymentHandler).findFirst().orElse(null));
    }
    @Bean(name = "orderChain")
    public OrderHandler orderChain() {
        if (steps == null || steps.isEmpty()) {
            throw new IllegalArgumentException("处理链步骤不能为空");
        }
        // 动态创建处理链
        OrderHandler firstHandler = null;
        OrderHandler lastHandler = null;
        for (String step : steps) {
            OrderHandler handler = handlerMap.get(step);
            if (handler == null) {
                throw new IllegalArgumentException("未找到处理器: " + step);
            }
            if (firstHandler == null) {
                firstHandler = handler;
            }
            if (lastHandler != null) {
                lastHandler.setNextHandler(handler);
            }
            lastHandler = handler;
        }
        if (lastHandler != null) {
            lastHandler.setNextHandler(null); // 最后一个处理器的 nextHandler 设置为 null
        }
        return firstHandler;
    }
    public void setSteps(List<String> steps) {
        this.steps = steps;
    }
}

说明

  1. 处理器映射:使用 Map<String, OrderHandler> 来存储处理器实例,通过 handlers 列表动态注入。这允许我们在配置中使用字符串名称引用处理器实例。
  2. 动态创建处理链:在 orderChain 方法中,根据 steps 配置的顺序动态创建处理链。每个处理器根据配置连接到下一个处理器。
  3. 配置检查:确保 steps 配置有效,不为空,并且在处理链创建时校验处理器是否存在。
  4. 处理器实例:在 OrderChainConfig 的构造函数中初始化处理器映射。确保 OrderValidationHandlerVerifyCouponHandlerShippingFeeHandlerTotalAmountHandlerProcessPaymentHandler 被正确注入并配置。

控制器接口优化

在优化后的控制器中,前端调用时返回 JSON 数据,jQuery 解析响应后通过 Bootstrap 弹出提示。

package com.icoderoad.orderchain.controller;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import com.icoderoad.orderchain.entity.OrderRequest;
import com.icoderoad.orderchain.entity.OrderRequest.Product;
import com.icoderoad.orderchain.handler.OrderHandler;
@RestController
public class OrderController {
    private final OrderHandler orderHandler;
    @Autowired
    public OrderController(@Qualifier("orderChain") OrderHandler orderHandler) {
        this.orderHandler = orderHandler;
    }
    @PostMapping("/processOrder")
    public Map<String, Object> processOrder() {
        Map<String, Object> response = new HashMap<>();
        try {
            OrderRequest request = new OrderRequest();
         // 创建商品对象
            Product product = new Product("10001", "手机", 2, new BigDecimal("20000.00"));
            // 创建商品列表并添加商品
            List<Product> productList = new ArrayList<>();
            productList.add(product);
            
            request.setShippingFee(new BigDecimal("200.00"));
            // 将商品列表设置到 OrderRequest 中
            request.setProductList(productList);
            orderHandler.handle(request);
            response.put("status", "success");
            response.put("message", "订单处理成功!");
        } catch (Exception e) {
            response.put("status", "error");
            response.put("message", "订单处理失败:" + e.getMessage());
        }
        return response;
    }
}

前端界面及 jQuery 调用 JSON 接口

在前端,我们使用 jQuery 发起 AJAX 请求,并通过 Bootstrap 提示处理结果。

src/main/resources/templates 目录下创建 index.html 文件:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>订单处理</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-5">
    <h2>电商订单处理</h2>
    <button class="btn btn-primary" id="processOrder">处理订单</button>
</div>
<!-- 弹出提示框 -->
<div class="modal fade" id="orderModal" tabindex="-1" aria-labelledby="orderModalLabel" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title" id="orderModalLabel">订单处理状态</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="modal-body">
                <!-- 动态显示订单处理结果 -->
            </div>
        </div>
    </div>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
    $(document).ready(function() {
        $("#processOrder").click(function() {
            $.ajax({
                url: '/processOrder',
                type: 'POST',
                success: function(response) {
                    // 根据返回状态显示提示
                    $(".modal-body").text(response.message);
                    $('#orderModal').modal('show');
                }
            });
        });
    });
</script>
</body>
</html>

总结

通过职责链模式,我们可以将复杂的订单处理流程解耦成多个独立的步骤,提升了代码的可维护性和扩展性。每个处理环节可以灵活配置和扩展,便于后续的功能迭代。同时,结合 Spring Boot 和前后端交互技术,进一步增强了系统的可用性与用户体验。


今天就讲到这里,如果有问题需要咨询,大家可以直接留言,我们会尽力为你解答。

相关文章
|
18天前
|
前端开发 Java 数据安全/隐私保护
用户登录前后端开发(一个简单完整的小项目)——SpringBoot与session验证(带前后端源码)全方位全流程超详细教程
文章通过一个简单的SpringBoot项目,详细介绍了前后端如何实现用户登录功能,包括前端登录页面的创建、后端登录逻辑的处理、使用session验证用户身份以及获取已登录用户信息的方法。
71 2
用户登录前后端开发(一个简单完整的小项目)——SpringBoot与session验证(带前后端源码)全方位全流程超详细教程
|
1月前
|
缓存 安全 Java
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
从底层源码入手,通过代码示例,追踪AnnotationConfigApplicationContext加载配置类、启动Spring容器的整个流程,并对IOC、BeanDefinition、PostProcesser等相关概念进行解释
106 24
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
|
10天前
|
NoSQL Java Redis
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
这篇文章介绍了如何使用Spring Boot整合Apache Shiro框架进行后端开发,包括认证和授权流程,并使用Redis存储Token以及MD5加密用户密码。
16 0
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
|
13天前
|
算法 前端开发 Java
支撑每秒数百万订单无压力,SpringBoot + Disruptor 太猛了!
本文详细介绍如何通过 Spring Boot 集成 Disruptor 实现每秒处理数百万订单的高性能系统。Disruptor 是一种无锁并发框架,采用环形缓冲区和无锁算法,提供极低延迟和高吞吐量。文章涵盖 Maven 配置、事件工厂、处理器及生产者实现,并通过 REST API 和 Thymeleaf 展示订单创建流程。Disruptor 在高并发场景下表现出色,是解决高性能并发处理的理想方案。
|
16天前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
101 2
|
2月前
|
消息中间件 缓存 Java
支撑每秒 600 万订单无压力,SpringBoot + Disruptor 太猛了!
【8月更文挑战第28天】在高度竞争且对性能要求极高的互联网时代,如何构建能够支撑海量订单处理的系统,是每一个技术团队都需要面对的挑战。今天,我们将深入探讨SpringBoot结合Disruptor这一高性能队列技术,如何实现每秒支撑600万订单量的壮举,分享其中的技术干货与实战经验。
74 5
|
2月前
|
Java 微服务 Spring
SpringBoot+Vue+Spring Cloud Alibaba 实现大型电商系统【分布式微服务实现】
文章介绍了如何利用Spring Cloud Alibaba快速构建大型电商系统的分布式微服务,包括服务限流降级等主要功能的实现,并通过注解和配置简化了Spring Cloud应用的接入和搭建过程。
SpringBoot+Vue+Spring Cloud Alibaba 实现大型电商系统【分布式微服务实现】
|
2月前
|
XML Java 应用服务中间件
SpringBoot启动流程解析
SpringBoot启动流程解析
39 0
|
3月前
|
Java 持续交付 Maven
Spring Boot程序的打包与运行:构建高效部署流程
构建高效的Spring Boot部署流程对于保障应用的快速、稳定上线至关重要。通过采用上述策略,您可以确保部署过程的自动化、可靠性和高效性,从而将专注点放在开发上面。无论是通过Maven的生命周期命令进行打包,还是通过容器技术对部署过程进行优化,选择正确的工具与实践是成功实现这一目标的关键。
154 2
|
3月前
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp的个性化推荐电商平台的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue+uniapp的个性化推荐电商平台的详细设计和实现(源码+lw+部署文档+讲解等)