👉 這是一個或許對你有用的社群
《專案實戰(影片)》:從書中學,往事上“練” 《網際網路高頻面試題》:面朝簡歷學習,春暖花開 《架構 x 系統設計》:摧枯拉朽,掌控面試高頻場景題 《精進 Java 學習指南》:系統學習,網際網路主流技術棧 《必讀 Java 原始碼專欄》:知其然,知其所以然

👉這是一個或許對你有用的開源專案國產 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 雙版本

一、背景
基於 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/
三、程式碼
<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>
ComponentAbstract.java
抽象類實現責任鏈的基礎import
com.liran.middle.liteflow.slot.Contxt;
import
lombok.extern.slf4j.Slf4j;
import
org.springframework.util.StopWatch;
/**
* 元件抽象類
*/
@Slf
4j
publicabstractclassComponentAbstract
{
publicvoidhandlerRequest(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);
}
elseif
(cost <=
50
) {
log.info(
"-----------監控統方法執行時間,執行 {} 方法, 用時一般: {} ms -----------"
, getClass(), cost);
}
elseif
(cost <=
500
) {
log.info(
"-----------監控統方法執行時間,執行 {} 方法, 用時延遲: {} ms -----------"
, getClass(), cost);
}
elseif
(cost <=
1000
) {
log.info(
"-----------監控統法執行時間,執行 {} 方法, 用時緩慢: {} ms -----------"
, getClass(), cost);
}
else
{
log.info(
"-----------監控方法執行時間,執行 {} 方法, 用時卡頓: {} ms -----------"
, getClass(), cost);
}
}
abstractpublicvoiddoHandler(Contxt contxt)
;
}
Test1.java
業務類1,繼承抽象類實現doHandler()
方法,在@Component
中設定類名1import
com.alibaba.fastjson.JSON;
import
com.liran.middle.liteflow.slot.Contxt;
import
lombok.extern.slf4j.Slf4j;
import
org.springframework.stereotype.Component;
@Component
(
"1"
)
@Slf
4j
publicclassTest1extendsComponentAbstract
{
@Override
publicvoiddoHandler(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
中設定類名2import
com.alibaba.fastjson.JSON;
import
com.liran.middle.liteflow.slot.Contxt;
import
lombok.extern.slf4j.Slf4j;
import
org.springframework.stereotype.Component;
@Component
(
"2"
)
@Slf
4j
publicclassTest2extendsComponentAbstract
{
@Override
publicvoiddoHandler(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
中設定類名3import
com.alibaba.fastjson.JSON;
import
com.liran.middle.liteflow.slot.Contxt;
import
lombok.extern.slf4j.Slf4j;
import
org.springframework.stereotype.Component;
@Component
(
"3"
)
@Slf
4j
publicclassTest3extendsComponentAbstract
{
@Override
publicvoiddoHandler(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
中設定類名4import
com.alibaba.fastjson.JSON;
import
com.liran.middle.liteflow.slot.Contxt;
import
lombok.extern.slf4j.Slf4j;
import
org.springframework.stereotype.Component;
@Component
(
"4"
)
@Slf
4j
publicclassTest4extendsComponentAbstract
{
@Override
publicvoiddoHandler(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
publicclassContxt
{
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
publicclassAopProxyUtilsimplementsApplicationContextAware
{
privatestatic
ApplicationContext applicationContext;
/**
* 實現ApplicationContextAware介面的setApplicationContext方法,
* 用於注入ApplicationContext。
*/
@Override
publicvoidsetApplicationContext(ApplicationContext context)throws BeansException
{
applicationContext = context;
}
/**
* 獲取指定類的代理物件,適用於需要事務或其他AOP增強的場景。
*
*
@param
clazz 要獲取代理的物件的類
*
@param
<T> 泛型標記
*
@return
代理物件例項
*/
publicstatic
<T>
T getProxyBean(Class<T> clazz)
{
if
(applicationContext ==
null
) {
thrownew
IllegalStateException(
"ApplicationContext not initialized."
);
}
return
applicationContext.getBean(clazz);
}
publicstatic Object getProxyBean(String name)
{
return
applicationContext.getBean(name);
}
}
LiteFlowController.java
用於測試,演示如何動態編排。呼叫介面http://localhost:8082/test/chain?index=2,1,3,4
傳入不同的index順序,業務邏輯中執行的順序也不同。import
com.alibaba.fastjson.JSON;
import
com.liran.middle.liteflow.component.pattern.chain.ComponentAbstract;
import
com.liran.middle.liteflow.slot.Contxt;
import
com.liran.middle.liteflow.utils.AopProxyUtils;
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
(value =
"/test"
)
@Slf
4j
publicclassLiteFlowController
{
/**
* 不使用框架,手動實現動態業務編排
*
*
@param
index 類名稱
*
@return
*/
@GetMapping
(value =
"chain"
)
public String pattern(@RequestParam String index)
{
Contxt contxt =
new
Contxt().builder()
.age(
"初始化"
)
.adrss(
"初始化"
)
.name(
"初始化"
)
.userid(
"初始化"
)
.age(
"初始化"
)
.build();
String[] split = index.split(
","
);
for
(String className : split) {
// 此處直接根據類名從 spring 管理的上下文中進行獲取。這裡的類名是子類註解@Component("1")中自定義的,如果沒有定義的話,預設使用類名
// 使用這種方式可以保證類名不重複。
ComponentAbstract msgHandler = (ComponentAbstract) AopProxyUtils.getProxyBean(className);
if
(ObjectUtils.isNotEmpty(msgHandler)) {
msgHandler.handlerRequest(contxt);
}
else
{
log.info(
"沒有找到對應的元件: {}"
, className);
}
}
return
JSON.toJSONString(contxt);
}
}
四、注意
https://liteflow.cc/pages/5816c5/
https://gitee.com/bryan31/liteflow-example





