用责任链模式拆分复杂接口功能

82 阅读5分钟

在实际项目中,接口往往包含多个功能点,不同客户需求不同,功能需要灵活组合。本文用最简单的语言介绍如何用责任链模式拆分接口功能,实现灵活的功能组合,并配备完整示例代码,方便中国程序员理解和使用。

一、什么是责任链模式?

责任链模式是一种设计模式,它把多个处理步骤(功能点)串成一条链,按顺序依次执行。每个处理步骤只关注自己的业务,处理完后把结果传给下一个,解耦了各个功能模块。

简单来说:

  • 请求发送者只负责发起请求
  • 多个处理者组成链条,依次处理请求
  • 每个处理者只管自己的功能,处理完传给下一个
  • 可以动态调整处理顺序,灵活应对不同需求

二、为什么用责任链模式拆分接口功能?

假设接口有十几个功能点,比如:

  • 参数校验
  • 系统配置检查
  • 基础数据入库
  • 核心数据入库
  • 发送消息到消息中心
  • 发送消息到MQ

不同客户需要不同功能组合。用责任链模式:

  • 每个功能点做成独立模块(组件)
  • 通过配置灵活调整执行顺序和功能开关
  • 方便维护和扩展,不用改动整体代码

三、责任链模式核心结构

角色作用说明
抽象处理者类定义处理请求的抽象方法,所有功能模块继承它
具体处理者类实现具体功能逻辑,如参数校验、数据入库等
上下文对象用于各个处理者之间传递数据,保持业务解耦
责任链管理器根据配置顺序,动态调用各个处理者

四、代码示例讲解

1. Maven依赖

<!-- 用于日志打印和JSON序列化 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.76</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.12</version>
</dependency>

2. 业务上下文类 Contxt.java

用于在责任链中传递和修改数据,字段可根据业务需求自由扩展。

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Contxt {
    private String name;
    private String age;
    private String address;
    private String userId;
}

3. 抽象处理者类 ComponentAbstract.java

定义统一的处理接口和执行时间监控,所有功能模块继承它。

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StopWatch;

@Slf4j
public abstract class ComponentAbstract {

    public void handlerRequest(Contxt contxt) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        // 执行具体业务逻辑
        this.doHandler(contxt);

        stopWatch.stop();
        long cost = stopWatch.getTotalTimeMillis();

        // 简单的执行时间日志分类
        if (cost <= 10) {
            log.info("执行 {} 方法,用时优秀: {} ms", getClass().getSimpleName(), cost);
        } else if (cost <= 50) {
            log.info("执行 {} 方法,用时一般: {} ms", getClass().getSimpleName(), cost);
        } else if (cost <= 500) {
            log.info("执行 {} 方法,用时延迟: {} ms", getClass().getSimpleName(), cost);
        } else if (cost <= 1000) {
            log.info("执行 {} 方法,用时缓慢: {} ms", getClass().getSimpleName(), cost);
        } else {
            log.info("执行 {} 方法,用时卡顿: {} ms", getClass().getSimpleName(), cost);
        }
    }

    // 子类必须实现的业务处理方法
    abstract public void doHandler(Contxt contxt);
}

4. 具体业务处理类示例(Test1.java)

继承抽象类,实现具体功能逻辑。用@Component注解注册到Spring容器,名称用于动态调用。

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Component("1")
@Slf4j
public class Test1 extends ComponentAbstract {
    @Override
    public void doHandler(Contxt contxt) {
        log.info("Test1 执行,当前上下文:{}", JSON.toJSONString(contxt));
        contxt.setName("Test1处理后");
        contxt.setAge("30");
        contxt.setAddress("北京");
        contxt.setUserId("user1");
    }
}

类似地,可以定义Test2、Test3、Test4等多个功能模块,分别实现不同业务。

5. Spring上下文工具类 AopProxyUtils.java

用于根据类名动态从Spring容器获取对应的组件实例,支持AOP代理。

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class AopProxyUtils implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        applicationContext = context;
    }

    public static <T> T getProxyBean(Class<T> clazz) {
        if (applicationContext == null) {
            throw new IllegalStateException("ApplicationContext未初始化");
        }
        return applicationContext.getBean(clazz);
    }

    public static Object getProxyBean(String name) {
        if (applicationContext == null) {
            throw new IllegalStateException("ApplicationContext未初始化");
        }
        return applicationContext.getBean(name);
    }
}

6. 控制器示例 LiteFlowController.java

通过接口传入执行顺序参数,实现动态业务编排。

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
@Slf4j
public class LiteFlowController {

    @GetMapping("chain")
    public String pattern(@RequestParam String index) {
        Contxt contxt = Contxt.builder()
                .age("初始化")
                .address("初始化")
                .name("初始化")
                .userId("初始化")
                .build();

        String[] sequence = index.split(",");

        for (String className : sequence) {
            ComponentAbstract handler = (ComponentAbstract) AopProxyUtils.getProxyBean(className);
            if (ObjectUtils.isNotEmpty(handler)) {
                handler.handlerRequest(contxt);
            } else {
                log.warn("未找到组件: {}", className);
            }
        }
        return JSON.toJSONString(contxt);
    }
}

调用示例:

http://localhost:8082/test/chain?index=2,1,3,4

表示先执行Test2,再执行Test1、Test3、Test4,顺序灵活可控。

五、扩展说明

  • 上下文类Contxt:可以根据业务需求添加更多字段,例如请求ID、用户信息、状态标志等,实现更复杂的数据传递。
  • 执行顺序配置:实际项目中,执行顺序可以存数据库或Redis,启动时加载,方便动态调整。
  • 性能监控:抽象类中集成了执行时间监控,帮助定位性能瓶颈。
  • AOP支持:通过Spring容器获取Bean,保证事务、日志等AOP功能生效。

六、总结

责任链模式能有效拆分复杂接口的多功能点,提升代码的灵活性和可维护性。通过抽象处理者和上下文传递,实现业务模块解耦和动态编排。示例代码简单明了,适合中国开发者快速上手。

七、完整示例代码仓库结构建议

src/main/java/com/example/chain/
    ComponentAbstract.java
    Contxt.java
    AopProxyUtils.java
    Test1.java
    Test2.java
    Test3.java
    Test4.java
    LiteFlowController.java

通过以上介绍和示例,您可以轻松实现一个灵活、可扩展的责任链模式业务处理框架,满足不同客户的个性化需求。