项目中有一个 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 T getProxyBean(Class clazz) {
if (applicationContext == null