专栏名称: 芋道源码
纯 Java 源码分享公众号,目前有「Dubbo」「SpringCloud」「Java 并发」「RocketMQ」「Sharding-JDBC」「MyCAT」「Elastic-Job」「SkyWalking」「Spring」等等
目录
相关文章推荐
芋道源码  ·  SpringBoot ... ·  12 小时前  
芋道源码  ·  3月,Java岗又爆了?! ·  12 小时前  
芋道源码  ·  为什么有的网站链接这么短? ·  昨天  
芋道源码  ·  DeepSeek本地部署,保姆级教程,带你打 ... ·  昨天  
51好读  ›  专栏  ›  芋道源码

SpringBoot 项目基于责任链模式实现复杂接口的解耦和动态编排

芋道源码  · 公众号  · Java  · 2025-03-04 14:00

正文

👉 这是一个或许对你有用 的社群

🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入 芋道快速开发平台 知识星球。 下面是星球提供的部分资料:

👉 这是一个或许对你有用的开源项目

国产 Star 破 10w+ 的开源项目,前端包括管理后台 + 微信小程序,后端支持单体和微服务架构。

功能涵盖 RBAC 权限、SaaS 多租户、数据权限、 商城 、支付、工作流、大屏报表、微信公众号、 ERP CRM AI 大模型 等等功能:

  • Boot 多模块架构:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • Cloud 微服务架构:https://gitee.com/zhijiantianya/yudao-cloud
  • 视频教程:https://doc.iocoder.cn
【国内首批】支持 JDK 17/21 + SpringBoot 3.3、JDK 8/11 + Spring Boot 2.7 双版本

来源:juejin.cn/post/
7402435366647758857


一、背景

项目中有一个 OpenApi 接口提供给客户(上游系统)调用。

这个接口中包含十几个功能点,比如:入参校验、系统配置校验、基本数据入库、核心数据入库、发送给消息中心、发送给 MQ.....

不同的客户对这个接口的要求也不同,有些功能不需要,有些需要添加特定功能。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

二、思路

  • 基于以上背景,考虑把十几个功能点进行拆分形成独立的功能。因此使用责任链模式实现。
  • 创建一个抽象类( ComponentAbstract.java ),每个拆分功能点继承抽象类形成子类。
  • 子类创建时,需要在 @Component("1") 注解中设置类名,如果不设置咋使用默认的(小驼峰)名称
  • 子类之间的数据通信使用自定义的上下文类( Contxt.java )子类中可以对上下文数据进行修改。(业务解耦)
  • 通过事先定义好的执行顺序,通过 spring 的上下文 ApplicationContext 根据子类名称循环获取子类对象,执行抽象类中 handlerRequest() 方法。
  • “事先定义好的执行顺序”,可以保存到数据库中项目启动的时候加载到内存,或者直接维护到Redis中。我这边直接使用接口进行演示: http://localhost:8082/test/chain?index=2,1,3,4

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud
  • 视频教程:https://doc.iocoder.cn/video/

三、代码

maven依赖,没有特别的依赖fastjson用于测试时打印日志


    com.alibaba
    fastjson
    1.2.76


    org.projectlombok
    lombok
    1.18.12

ComponentAbstract.java 抽象类实现责任链的基础

import com.liran.middle.liteflow.slot.Contxt;
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(), cost);
        } else if (cost <= 50) {
            log.info("-----------监控统方法执行时间,执行 {} 方法, 用时一般: {} ms -----------", getClass(), cost);
        } else if (cost <= 500) {
            log.info("-----------监控统方法执行时间,执行 {} 方法, 用时延迟: {} ms -----------", getClass(), cost);
        } else if (cost <= 1000) {
            log.info("-----------监控统法执行时间,执行 {} 方法, 用时缓慢: {} ms -----------", getClass(), cost);
        } else {
            log.info("-----------监控方法执行时间,执行 {} 方法, 用时卡顿: {} ms -----------", getClass(), cost);
        }
    }
    abstract public void doHandler(Contxt contxt);
}

Test1.java 业务类1,继承抽象类实现 doHandler() 方法,在 @Component 中设置类名1

import com.alibaba.fastjson.JSON;
import com.liran.middle.liteflow.slot.Contxt;
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-顺序1-上下文内容为:{}", JSON.toJSONString(contxt));
        contxt.setName("Test1");
        contxt.setAge("Test1");
        contxt.setAdrss("Test1");
        contxt.setUserid("Test1");
    }
}

Test2.java 业务类2,继承抽象类实现 doHandler() 方法,在 @Component 中设置类名2

import com.alibaba.fastjson.JSON;
import com.liran.middle.liteflow.slot.Contxt;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Component("2")
@Slf4j
public class Test2 extends ComponentAbstract {

    @Override
    public void doHandler(Contxt contxt) {
        log.info("Test2-顺序2-上下文内容为:{}", JSON.toJSONString(contxt));
        contxt.setName("Test2");
        contxt.setAge("Test2");
        contxt.setAdrss("Test2");
        contxt.setUserid("Test2");
    }
}

Test3.java 业务类3,继承抽象类实现 doHandler() 方法,在 @Component 中设置类名3

import com.alibaba.fastjson.JSON;
import com.liran.middle.liteflow.slot.Contxt;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Component("3")
@Slf4j
public class Test3 extends ComponentAbstract {
    @Override
    public void doHandler(Contxt contxt) {
        log.info("Test3-顺序3-上下文内容为:{}", JSON.toJSONString(contxt));
        contxt.setName("Test3");
        contxt.setAge("Test3");
        contxt.setAdrss("Test3");
        contxt.setUserid("Test3");
    }
}

Test4.java 业务类4,继承抽象类实现 doHandler() 方法,在 @Component 中设置类名4

import com.alibaba.fastjson.JSON;
import com.liran.middle.liteflow.slot.Contxt;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Component("4")
@Slf4j
public class Test4 extends ComponentAbstract {

    @Override
    public void doHandler(Contxt contxt) {
        log.info("Test4-顺序4-上下文内容为:{}", JSON.toJSONString(contxt));
        contxt.setName("Test4");
        contxt.setAge("Test4");
        contxt.setAdrss("Test4");
        contxt.setUserid("Test4");
    }
}

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 adrss;
    private String userid;
}

AopProxyUtils.java ,spring 管理的上下文,用于根据类名获取类实体。

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;

    /**
     * 实现ApplicationContextAware接口的setApplicationContext方法,
     * 用于注入ApplicationContext。
     */

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

    /**
     * 获取指定类的代理对象,适用于需要事务或其他AOP增强的场景。
     *
     * @param clazz 要获取代理的对象的类
     * @param    泛型标记
     * @return 代理对象实例
     */

    public static  getProxyBean(Class clazz) {
        if (applicationContext == null






请到「今天看啥」查看全文