SpringBoot使用设计模式一责任链模式

一、前言

在日常开发中,经常会遇到需要对某个请求进行多步处理的场景。例如用户注册流程,需要依次完成参数校验、手机号合法性验证、用户信息查重、密码加密、数据入库等操作;再比如接口请求的拦截处理,需要经过token验证、权限校验、限流控制、日志记录等步骤。

如果采用传统的写法,可能会出现如下的代码:

public R<?> register(UserRegisterDTO registerDTO) {
    // 1. 参数校验
    if (registerDTO.getUsername() == null || registerDTO.getPassword() == null) {
        return R.fail("用户名或密码不能为空");
    }
    // 2. 手机号验证
    if (!Pattern.matches("^1[3-9]\\d{9}$", registerDTO.getPhone())) {
        return R.fail("手机号格式不正确");
    }
    // 3. 用户查重
    if (userMapper.existsByUsername(registerDTO.getUsername())) {
        return R.fail("用户名已存在");
    }
    // 4. 密码加密
    registerDTO.setPassword(passwordEncoder.encode(registerDTO.getPassword()));
    // 5. 数据入库
    userMapper.insert(convertToEntity(registerDTO));
    return R.success("注册成功");
}

这种写法存在明显的问题:代码臃肿且耦合度高,每新增一个校验步骤都需要修改原有方法,违反开闭原则;步骤之间的顺序固定,无法灵活调整;单个步骤的逻辑无法复用,若其他场景需要用到其中某个校验规则,只能重复编写。
针对这种多步骤、可组合、可扩展的处理场景,责任链模式是绝佳的解决方案。

二、责任链模式

责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,它将请求的处理者串联成一条链,请求沿着这条链依次传递,直到链中的某个处理者能够处理该请求为止。

核心角色

  • 抽象处理者(Handler):定义处理请求的接口,包含一个指向下一处理者的引用,以及处理请求的抽象方法;
  • 具体处理者(Concrete Handler):实现抽象处理者接口,封装具体的处理逻辑,判断自身是否能处理请求,若能处理则处理,否则将请求传递给下一个处理者;
  • 客户端(Client):创建责任链,并将请求提交到链的首端,无需关心请求的具体处理过程。

使用场景

  • 一个请求需要经过多个步骤处理,且步骤顺序可调整;
  • 不确定请求的处理者或处理顺序,需要动态组合处理流程;
  • 希望避免请求的发送者与接收者之间的紧耦合。

优点

  • 降低耦合度:请求发送者与处理者解耦,发送者无需知道请求的处理者及处理流程;
  • 增强灵活性:可动态调整责任链的顺序、新增或移除处理者,符合开闭原则;
  • 便于复用:每个处理者职责单一,可在不同的责任链中复用。

缺点

  • 请求可能无法被处理:若责任链中没有处理者能处理请求,可能导致请求丢失(需提前设计兜底方案);
  • 调试难度增加:请求的处理过程分散在多个处理者中,排查问题时需要跟踪整个链条。

三、实现案例

以用户注册流程为例,使用责任链模式重构代码,实现参数校验、手机号验证、用户查重、密码加密、数据入库等步骤的解耦与灵活组合。

3.1 定义请求实体与响应工具类

首先定义用户注册的请求参数实体,包含用户名、密码、手机号等核心字段:

@Data
@ApiModel(value = "用户注册请求")
public class UserRegisterDTO implements Serializable {
    @ApiModelProperty(value = "用户名")
    private String username;

    @ApiModelProperty(value = "密码")
    private String password;

    @ApiModelProperty(value = "手机号")
    private String phone;
}

定义统一的响应工具类(复用常用的R对象):

public class R<T> implements Serializable {
    private boolean success;
    private String message;
    private T data;

    // 成功响应静态方法
    public static <T> R<T> success(String message, T data) {
        R<T> r = new R<>();
        r.setSuccess(true);
        r.setMessage(message);
        r.setData(data);
        return r;
    }

    public static <T> R<T> success(String message) {
        return success(message, null);
    }

    // 失败响应静态方法
    public static <T> R<T> fail(String message) {
        R<T> r = new R<>();
        r.setSuccess(false);
        r.setMessage(message);
        return r;
    }

    // getter/setter省略
}

3.2 定义抽象处理者(Handler)

创建抽象处理者接口,定义处理请求的方法和设置下一个处理者的方法:

/**
 * 注册流程抽象处理者
 */
public interface RegisterHandler {
    /**
     * 处理注册请求
     * @param registerDTO 注册请求参数
     * @return 处理结果
     */
    R<?> handle(UserRegisterDTO registerDTO);

    /**
     * 设置下一个处理者
     * @param nextHandler 下一个处理者
     * @return 下一个处理者(支持链式调用)
     */
    RegisterHandler setNext(RegisterHandler nextHandler);
}

为了简化具体处理者的实现,创建抽象基类,实现setNext方法和请求传递逻辑:

/**
 * 注册流程处理者基类(实现默认的链式调用逻辑)
 */
public abstract class AbstractRegisterHandler implements RegisterHandler {
    // 下一个处理者
    protected RegisterHandler nextHandler;

    @Override
    public RegisterHandler setNext(RegisterHandler nextHandler) {
        this.nextHandler = nextHandler;
        // 返回下一个处理者,支持链式构建责任链
        return nextHandler;
    }

    /**
     * 处理请求的模板方法:当前处理者处理完成后,传递给下一个处理者
     */
    @Override
    public R<?> handle(UserRegisterDTO registerDTO) {
        // 子类实现具体处理逻辑
        R<?> result = doHandle(registerDTO);
        // 若当前处理成功且存在下一个处理者,则继续传递请求
        if (result.isSuccess() && nextHandler != null) {
            return nextHandler.handle(registerDTO);
        }
        // 处理失败或无下一个处理者,直接返回结果
        return result;
    }

    /**
     * 具体处理逻辑(由子类实现)
     */
    protected abstract R<?> doHandle(UserRegisterDTO registerDTO);
}

3.3 实现具体处理者(Concrete Handler)

针对注册流程的每个步骤,创建对应的具体处理者类,继承抽象基类并实现doHandle方法:

3.3.1 参数校验处理者

@Component
@Slf4j
public class ParamValidateHandler extends AbstractRegisterHandler {
    @Override
    protected R<?> doHandle(UserRegisterDTO registerDTO) {
        log.info("执行参数校验处理");
        // 校验用户名、密码、手机号非空
        if (StringUtils.isEmpty(registerDTO.getUsername())) {
            return R.fail("用户名不能为空");
        }
        if (StringUtils.isEmpty(registerDTO.getPassword())) {
            return R.fail("密码不能为空");
        }
        if (StringUtils.isEmpty(registerDTO.getPhone())) {
            return R.fail("手机号不能为空");
        }
        // 校验密码长度(6-20位)
        if (registerDTO.getPassword().length() < 6 || registerDTO.getPassword().length() > 20) {
            return R.fail("密码长度必须在6-20位之间");
        }
        return R.success("参数校验通过");
    }
}

3.3.2 手机号格式验证处理者

@Component
@Slf4j
public class PhoneValidateHandler extends AbstractRegisterHandler {
    // 手机号正则表达式
    private static final Pattern PHONE_PATTERN = Pattern.compile("^1[3-9]\\d{9}$");

    @Override
    protected R<?> doHandle(UserRegisterDTO registerDTO) {
        log.info("执行手机号格式验证");
        if (!PHONE_PATTERN.matcher(registerDTO.getPhone()).matches()) {
            return R.fail("手机号格式不正确");
        }
        return R.success("手机号格式验证通过");
    }
}

3.3.3 用户查重处理者

@Component
@Slf4j
public class UserDuplicateCheckHandler extends AbstractRegisterHandler {
    @Autowired
    private UserMapper userMapper;

    @Override
    protected R<?> doHandle(UserRegisterDTO registerDTO) {
        log.info("执行用户查重处理");
        // 检查用户名是否已存在
        if (userMapper.existsByUsername(registerDTO.getUsername())) {
            return R.fail("用户名已存在");
        }
        // 检查手机号是否已绑定
        if (userMapper.existsByPhone(registerDTO.getPhone())) {
            return R.fail("手机号已被注册");
        }
        return R.success("用户查重通过");
    }
}

3.3.4 密码加密处理者

@Component
@Slf4j
public class PasswordEncodeHandler extends AbstractRegisterHandler {
    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    protected R<?> doHandle(UserRegisterDTO registerDTO) {
        log.info("执行密码加密处理");
        // 对密码进行BCrypt加密
        String encodedPassword = passwordEncoder.encode(registerDTO.getPassword());
        // 更新加密后的密码到请求对象
        registerDTO.setPassword(encodedPassword);
        return R.success("密码加密成功");
    }
}

3.3.5 数据入库处理者

@Component
@Slf4j
public class UserSaveHandler extends AbstractRegisterHandler {
    @Autowired
    private UserMapper userMapper;

    @Override
    protected R<?> doHandle(UserRegisterDTO registerDTO) {
        log.info("执行用户数据入库处理");
        // 转换DTO为实体类
        User user = new User();
        user.setUsername(registerDTO.getUsername());
        user.setPassword(registerDTO.getPassword());
        user.setPhone(registerDTO.getPhone());
        user.setCreateTime(LocalDateTime.now());
        // 入库操作
        userMapper.insert(user);
        return R.success("用户注册成功", user.getId());
    }
}

3.4 构建责任链(工厂模式)

创建责任链工厂类,负责组装具体处理者,形成完整的处理链,避免客户端直接依赖多个处理者:

@Component
public class RegisterChainFactory {
    // 注入所有具体处理者(Spring自动扫描AbstractRegisterHandler的实现类)
    @Autowired
    private List<AbstractRegisterHandler> handlerList;

    /**
     * 构建责任链(固定顺序:参数校验→手机号验证→用户查重→密码加密→数据入库)
     * @return 责任链的首端处理者
     */
    public RegisterHandler buildChain() {
        // 校验处理者列表非空
        if (CollectionUtils.isEmpty(handlerList)) {
            throw new IllegalStateException("注册流程处理者不能为空");
        }

        // 按指定顺序排序(通过类名匹配顺序)
        List<AbstractRegisterHandler> sortedHandlers = sortHandlers();

        // 组装责任链:依次设置每个处理者的下一个节点
        for (int i = 0; i < sortedHandlers.size() - 1; i++) {
            sortedHandlers.get(i).setNext(sortedHandlers.get(i + 1));
        }

        // 返回链的首端(第一个处理者)
        return sortedHandlers.get(0);
    }

    /**
     * 按业务顺序排序处理者
     */
    private List<AbstractRegisterHandler> sortHandlers() {
        // 定义处理者的顺序:参数校验→手机号验证→用户查重→密码加密→数据入库
        Map<Class<?>, Integer> orderMap = new HashMap<>();
        orderMap.put(ParamValidateHandler.class, 1);
        orderMap.put(PhoneValidateHandler.class, 2);
        orderMap.put(UserDuplicateCheckHandler.class, 3);
        orderMap.put(PasswordEncodeHandler.class, 4);
        orderMap.put(UserSaveHandler.class, 5);

        // 按orderMap定义的顺序排序
        return handlerList.stream()
                .sorted(Comparator.comparingInt(handler -> 
                        orderMap.getOrDefault(handler.getClass(), Integer.MAX_VALUE)))
                .collect(Collectors.toList());
    }
}

3.5 控制器层调用(客户端)

控制器通过工厂获取责任链,发起注册请求,无需关心具体的处理步骤:

@RestController
@RequestMapping("/user")
@Slf4j
public class UserRegisterController {

    @Autowired
    private RegisterChainFactory registerChainFactory;

    /**
     * 用户注册接口
     */
    @PostMapping("/register")
    public R<?> register(@RequestBody @Valid UserRegisterDTO registerDTO) {
        log.info("接收用户注册请求:{}", registerDTO);
        // 从工厂获取责任链
        RegisterHandler registerChain = registerChainFactory.buildChain();
        // 发起请求(从链首开始传递)
        return registerChain.handle(registerDTO);
    }
}

四、责任链模式的进阶用法

4.1 动态调整责任链

实际开发中,可能需要根据不同场景动态调整责任链的步骤(例如某些场景不需要手机号验证)。可以通过工厂类的扩展方法实现:

/**
 * 动态构建责任链(支持指定需要的处理者)
 * @param includeHandlerClasses 需要包含的处理者类
 * @return 自定义责任链
 */
public RegisterHandler buildDynamicChain(Set<Class<? extends AbstractRegisterHandler>> includeHandlerClasses) {
    if (CollectionUtils.isEmpty(handlerList) || CollectionUtils.isEmpty(includeHandlerClasses)) {
        throw new IllegalArgumentException("处理者列表或需要包含的处理者不能为空");
    }

    // 筛选需要的处理者并排序
    List<AbstractRegisterHandler> selectedHandlers = handlerList.stream()
            .filter(handler -> includeHandlerClasses.contains(handler.getClass()))
            .sorted(Comparator.comparingInt(handler -> 
                    orderMap.getOrDefault(handler.getClass(), Integer.MAX_VALUE)))
            .collect(Collectors.toList());

    // 组装责任链
    for (int i = 0; i < selectedHandlers.size() - 1; i++) {
        selectedHandlers.get(i).setNext(selectedHandlers.get(i + 1));
    }

    return selectedHandlers.get(0);
}

控制器中动态指定处理步骤:

/**
 * 无需手机号验证的注册接口(例如后台管理系统创建用户)
 */
@PostMapping("/register/no-phone-validate")
public R<?> registerWithoutPhoneValidate(@RequestBody @Valid UserRegisterDTO registerDTO) {
    log.info("接收无需手机号验证的注册请求:{}", registerDTO);
    // 指定需要包含的处理者(排除PhoneValidateHandler)
    Set<Class<? extends AbstractRegisterHandler>> includeHandlers = new HashSet<>();
    includeHandlers.add(ParamValidateHandler.class);
    includeHandlers.add(UserDuplicateCheckHandler.class);
    includeHandlers.add(PasswordEncodeHandler.class);
    includeHandlers.add(UserSaveHandler.class);
    // 构建动态责任链
    RegisterHandler registerChain = registerChainFactory.buildDynamicChain(includeHandlers);
    return registerChain.handle(registerDTO);
}

4.2 注解式配置责任链顺序

为了避免在工厂类中硬编码处理者顺序,可通过自定义注解标记处理者的优先级:

/**
 * 责任链顺序注解
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface HandlerOrder {
    // 顺序值越小,优先级越高
    int value();
}

在具体处理者上添加注解:

@Component
@Slf4j
@HandlerOrder(1) // 优先级1:最先执行
public class ParamValidateHandler extends AbstractRegisterHandler {
    // ... 原有逻辑
}

@Component
@Slf4j
@HandlerOrder(2) // 优先级2:次之
public class PhoneValidateHandler extends AbstractRegisterHandler {
    // ... 原有逻辑
}

改造工厂类的排序逻辑:

/**
 * 基于注解排序处理者
 */
private List<AbstractRegisterHandler> sortHandlersByAnnotation() {
    return handlerList.stream()
            .sorted(Comparator.comparingInt(handler -> {
                HandlerOrder annotation = handler.getClass().getAnnotation(HandlerOrder.class);
                return annotation != null ? annotation.value() : Integer.MAX_VALUE;
            }))
            .collect(Collectors.toList());
}

4.3 结合配置文件实现可配置责任链

将责任链的处理者顺序配置在application.yml中,实现无需修改代码即可调整流程:

# 注册责任链配置:key为处理者类名(简化版),value为顺序
register:
  chain:
    order:
      ParamValidateHandler: 1
      PhoneValidateHandler: 2
      UserDuplicateCheckHandler: 3
      PasswordEncodeHandler: 4
      UserSaveHandler: 5

定义配置实体类:

@Data
@Component
@ConfigurationProperties(prefix = "register.chain")
public class RegisterChainProperties {
    // 处理者顺序配置:key=处理者类名(简单类名),value=顺序
    private Map<String, Integer> order;
}

改造工厂类的排序逻辑:

@Component
public class RegisterChainFactory {
    @Autowired
    private List<AbstractRegisterHandler> handlerList;

    @Autowired
    private RegisterChainProperties chainProperties;

    /**
     * 基于配置文件排序处理者
     */
    private List<AbstractRegisterHandler> sortHandlersByConfig() {
        Map<String, Integer> orderConfig = chainProperties.getOrder();
        if (CollectionUtils.isEmpty(orderConfig)) {
            throw new IllegalStateException("责任链顺序配置不能为空");
        }

        return handlerList.stream()
                .sorted(Comparator.comparingInt(handler -> {
                    // 获取处理者的简单类名
                    String className = handler.getClass().getSimpleName();
                    return orderConfig.getOrDefault(className, Integer.MAX_VALUE);
                }))
                .collect(Collectors.toList());
    }
}

4.4 责任链的中断与兜底处理

为了避免请求丢失,可在责任链末尾添加兜底处理者,确保所有请求都有响应;同时支持手动中断责任链(例如某些特殊场景需要终止流程):

4.4.1 兜底处理者

@Component
@Slf4j
@HandlerOrder(6) // 最后执行
public class FallbackHandler extends AbstractRegisterHandler {
    @Override
    protected R<?> doHandle(UserRegisterDTO registerDTO) {
        log.warn("注册流程未正常完成,执行兜底处理");
        return R.fail("注册失败,请联系客服");
    }
}

4.4.2 支持手动中断责任链

在抽象基类中添加中断方法,允许具体处理者主动终止流程:

public abstract class AbstractRegisterHandler implements RegisterHandler {
    protected RegisterHandler nextHandler;
    // 标记是否中断责任链
    private boolean interrupted = false;

    /**
     * 中断责任链
     */
    protected void interrupt() {
        this.interrupted = true;
    }

    @Override
    public R<?> handle(UserRegisterDTO registerDTO) {
        R<?> result = doHandle(registerDTO);
        // 若已中断或无下一个处理者,直接返回
        if (interrupted || nextHandler == null) {
            return result;
        }
        // 继续传递请求
        return nextHandler.handle(registerDTO);
    }

    // ... 其他原有方法
}

在具体处理者中使用:

@Component
@Slf4j
public class SpecialUserHandler extends AbstractRegisterHandler {
    @Autowired
    private SpecialUserService specialUserService;

    @Override
    protected R<?> doHandle(UserRegisterDTO registerDTO) {
        log.info("检测特殊用户");
        if (specialUserService.isSpecialUser(registerDTO.getUsername())) {
            // 特殊用户直接注册成功,中断责任链
            interrupt();
            return R.success("特殊用户快速注册成功");
        }
        return R.success("非特殊用户,继续流程");
    }
}

五、总结

本文通过用户注册场景,详细介绍了责任链模式在SpringBoot中的落地实现,主要收获如下:

  • 解耦处理步骤:将多步处理逻辑拆分为独立的处理者类,每个类职责单一,便于维护和复用;
  • 增强灵活性:支持动态调整责任链的顺序、新增/移除处理者,无需修改核心业务代码,符合开闭原则;
  • 简化客户端调用:客户端只需发起请求,无需关心处理流程和处理者,降低了代码耦合;
  • 支持多种进阶用法:通过注解、配置文件、动态组装等方式,满足不同场景的需求。

责任链模式的适用场景非常广泛,除了注册流程,还可用于接口请求拦截、数据校验、工作流审批、日志收集等场景。在实际开发中,结合工厂模式、注解、配置文件等技术,可以进一步提升责任链的灵活性和可维护性,是优化多步骤业务流程的优选方案。

posted @ 2026-01-06 20:42  夏尔_717  阅读(14)  评论(0)    收藏  举报