巧妙利用SpringBoot責任連模式,讓程式設計事半功倍!

👉 這是一個或許對你有用的社群
🐱 一對一交流/面試小冊/簡歷最佳化/求職解惑,歡迎加入芋道快速開發平臺知識星球。下面是星球提供的部分資料:
👉這是一個或許對你有用的開源專案
國產 Star 破 10w+ 的開源專案,前端包括管理後臺 + 微信小程式,後端支援單體和微服務架構。
功能涵蓋 RBAC 許可權、SaaS 多租戶、資料許可權、商城、支付、工作流、大屏報表、微信公眾號、CRM 等等功能:
  • Boot 倉庫:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • Cloud 倉庫:https://gitee.com/zhijiantianya/yudao-cloud
  • 影片教程:https://doc.iocoder.cn
【國內首批】支援 JDK 21 + SpringBoot 3.2.2、JDK 8 + Spring Boot 2.7.18 雙版本 

一、什麼是責任鏈模式?

責任鏈模式(Chain of Responsibility Pattern),顧名思義,為請求者和接受者之間建立一條物件處理鏈路,避免請求傳送者與接收者耦合在一起!

責任鏈模式,是一種實用性非常強的設計模式,比較典型的應用場景有:
  • Apache Tomcat 對 Encoding 編碼處理的處理
  • SpringBoot ⾥⾯的攔截器、過濾器鏈
  • netty 中的處理鏈
  • 支付風控的機制
  • ⽇志處理級別
尤其是當程式的處理流程很長的時候,採用責任鏈設計模式,不僅實現優雅,而且易複用可擴充套件!
今天我們就一起來了解一下在 SpringBoot 中如何應用責任鏈模式!
基於 Spring Boot + MyBatis Plus + Vue & Element 實現的後臺管理系統 + 使用者小程式,支援 RBAC 動態許可權、多租戶、資料許可權、工作流、三方登入、支付、簡訊、商城等功能
  • 專案地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 影片教程:https://doc.iocoder.cn/video/

二、程式碼實踐

在 SpringBoot 中,責任鏈模式的實踐方式有好幾種,今天我們主要抽三種實踐方式給大家介紹。
我們以某下單流程為例,將其切成多個獨立檢查邏輯,可能會經過的資料驗證處理流程如下:

採用責任鏈設計模式來程式設計,程式碼實踐如下!

2.1、方式一

首先,我們定義一個簡易版的下單物件OrderContext
publicclassOrderContext{/**     * 請求唯一序列ID     */private String seqId;/**     * 使用者ID     */private String userId;/**     * 產品skuId     */private Long skuId;/**     * 下單數量     */private Integer amount;/**     * 使用者收貨地址ID     */private String userAddressId;//..set、get}
然後,我們定義一個數據處理介面OrderHandleIntercept,用於標準化執行!
publicinterfaceOrderHandleIntercept{/**     * 指定執行順序     * @return     */intsort();/**     * 對引數進行處理     * @param context     * @return     */OrderAddContext handle(OrderAddContext context);}
接著,我們分別建立三個不同的介面實現類,並指定執行順序,內容如下:
  • RepeatOrderHandleInterceptService:用於重複下單的邏輯驗證
  • ValidOrderHandleInterceptService:用於驗證請求引數是否合法
  • BankOrderHandleInterceptService:用於檢查客戶賬戶餘額是否充足
@ComponentpublicclassRepeatOrderHandleInterceptServiceimplementsOrderHandleIntercept{@Overridepublicintsort(){//用於重複下單的邏輯驗證,在執行順序為1return1;    }@Overridepublic OrderAddContext handle(OrderAddContext context){        System.out.println("透過seqId,檢查客戶是否重複下單");return context;    }}@ComponentpublicclassValidOrderHandleInterceptServiceimplementsOrderHandleIntercept{@Overridepublicintsort(){//用於驗證請求引數是否合法,執行順序為2return2;    }@Overridepublic OrderAddContext handle(OrderAddContext context){        System.out.println("檢查請求引數,是否合法,並且獲取客戶的銀行賬戶");return context;    }}@ComponentpublicclassBankOrderHandleInterceptServiceimplementsOrderHandleIntercept{@Overridepublicintsort(){//用於檢查客戶賬戶餘額是否充足,在執行順序為3return3;    }@Overridepublic OrderAddContext handle(OrderAddContext context){        System.out.println("檢查銀行賬戶是否合法,呼叫銀行系統檢查銀行賬戶餘額是否滿足下單金額");return context;    }}
再然後,我們還需要建立一個訂單資料驗證管理器OrderHandleChainService,用於管理這些實現類。
@ComponentpublicclassOrderHandleChainServiceimplementsApplicationContextAware{private List<OrderHandleIntercept> handleList = new ArrayList<>();@OverridepublicvoidsetApplicationContext(ApplicationContext applicationContext)throws BeansException {//獲取指定的介面實現類,並按照sort進行排序,放入List中        Map<String, OrderHandleIntercept> serviceMap = applicationContext.getBeansOfType(OrderHandleIntercept.class);        handleList = serviceMap.values().stream()                .sorted(Comparator.comparing(OrderHandleIntercept::sort))                .collect(Collectors.toList());    }/**     * 執行處理     * @param context     * @return     */public OrderAddContext execute(OrderAddContext context){for (OrderHandleIntercept handleIntercept : handleList) {            context =handleIntercept.handle(context);        }return context;    }}
最後,我們編寫單元測試來看看效果如何!
@RunWith(SpringRunner.class)@SpringBootTestpublicclassCalculatorServiceTest{@Autowiredprivate OrderHandleChainService orderHandleChainService;@Testpublicvoidtest(){        orderHandleChainService.execute(new OrderAddContext());    }}
執行結果如下:
透過seqId,檢查客戶是否重複下單檢查請求引數,是否合法,並且獲取客戶的銀行賬戶檢查銀行賬戶是否合法,呼叫銀行系統檢查銀行賬戶餘額是否滿足下單金額
如果需要繼續加驗證流程或者處理流程,只需要重新實現OrderHandleIntercept介面就行,其他的程式碼無需改動!
當然,有的同學可能覺得這種方法用的不習慣,不喜歡透過sort()來指定順序,也可以透過如下方式進行手動add排序。
@ComponentpublicclassOrderHandleChainService{private List<OrderHandleIntercept> handleList = new ArrayList<>();@Autowiredprivate ValidOrderHandleInterceptService validOrderHandleInterceptService;@Autowiredprivate RepeatOrderHandleInterceptService repeatOrderHandleInterceptService;@Autowiredprivate BankOrderHandleInterceptService bankOrderHandleInterceptService;@PostConstructpublicvoidinit(){//依次手動add物件        handleList.add(repeatOrderHandleInterceptService);        handleList.add(validOrderHandleInterceptService);        handleList.add(bankOrderHandleInterceptService);    }/**     * 執行處理     * @param context     * @return     */public OrderAddContext execute(OrderAddContext context){for (OrderHandleIntercept handleIntercept : handleList) {            context =handleIntercept.handle(context);        }return context;    }}

2.2、方式二

第二種實現方式,就更簡單了,我們透過註解@Order來指定排序,代替手動方法排序sort(),操作方式如下:
/** * 指定注入順序為1 * */@Order(1)@ComponentpublicclassRepeatOrderHandleInterceptServiceimplementsOrderHandleIntercept{//...省略}/** * 指定注入順序為2 * */@Order(2)@ComponentpublicclassValidOrderHandleInterceptServiceimplementsOrderHandleIntercept{//...省略}/** * 指定注入順序為3 * */@Order(3)@ComponentpublicclassBankOrderHandleInterceptServiceimplementsOrderHandleIntercept{//...省略}@ComponentpublicclassOrderHandleChainService{@Autowiredprivate List<OrderHandleIntercept> handleList;/**     * 執行處理     * @param context     * @return     */public OrderAddContext execute(OrderAddContext context){for (OrderHandleIntercept handleIntercept : handleList) {            context =handleIntercept.handle(context);        }return context;    }}
執行單元測試,你會發現結果與上面執行的結果一致,原因Springioc容器,支援透過Map或者List來直接注入物件,省去自己排序。

2.3、方式三

透過定義抽象類來實現責任鏈設計模式,還是以上面的案例為例,我們需要先定義一個抽象類,比如AbstractOrderHandle
publicabstractclassAbstractOrderHandle{/**     * 責任鏈,下一個連結節點     */private AbstractOrderHandle next;/**     * 執行入口     * @param context     * @return     */public OrderAddContext execute(OrderAddContext context){        context = handle(context);// 判斷是否還有下個責任鏈節點,沒有的話,說明已經是最後一個節點if(getNext() != null){            getNext().execute(context);        }return context;    }/**     * 對引數進行處理     * @param context     * @return     */publicabstract OrderAddContext handle(OrderAddContext context);public AbstractOrderHandle getNext(){return next;    }publicvoidsetNext(AbstractOrderHandle next){this.next = next;    }}
然後,分別建立三個處理類,並排好序號。
@Order(1)@ComponentpublicclassRepeatOrderHandleextendsAbstractOrderHandle{@Overridepublic OrderAddContext handle(OrderAddContext context){        System.out.println("透過seqId,檢查客戶是否重複下單");return context;    }}@Order(2)@ComponentpublicclassValidOrderHandleextendsAbstractOrderHandle{@Overridepublic OrderAddContext handle(OrderAddContext context){        System.out.println("檢查請求引數,是否合法,並且獲取客戶的銀行賬戶");return context;    }}@Order(3)@ComponentpublicclassBankOrderHandleextendsAbstractOrderHandle{@Overridepublic OrderAddContext handle(OrderAddContext context){        System.out.println("檢查銀行賬戶是否合法,呼叫銀行系統檢查銀行賬戶餘額是否滿足下單金額");return context;    }}
接著,建立一個責任鏈管理器,比如OrderHandleManager
@ComponentpublicclassOrderHandleManager{@Autowiredprivate List<AbstractOrderHandle> orderHandleList;@PostConstructpublicvoidinit(){//如果List沒有按照@Order註解方式排序,可以透過如下方式手動排序        Collections.sort(orderHandleList, AnnotationAwareOrderComparator.INSTANCE);int size = orderHandleList.size();for (int i = 0; i < size; i++) {if(i == size -1){                orderHandleList.get(i).setNext(null);            } else {                orderHandleList.get(i).setNext(orderHandleList.get(i + 1));            }        }    }/**     * 執行處理     * @param context     * @return     */public OrderAddContext execute(OrderAddContext context){        context = orderHandleList.get(0).execute(context);return context;    }}
最後,我們編寫單元測試,來看看效果。
@RunWith(SpringRunner.class)@SpringBootTestpublicclassCalculatorServiceTest{@Autowiredprivate OrderHandleManager orderHandleManager;@Testpublicvoidtest(){        orderHandleManager.execute(new OrderAddContext());    }}
執行結果與預期一致!
透過seqId,檢查客戶是否重複下單檢查請求引數,是否合法,並且獲取客戶的銀行賬戶檢查銀行賬戶是否合法,呼叫銀行系統檢查銀行賬戶餘額是否滿足下單金額
基於 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實現的後臺管理系統 + 使用者小程式,支援 RBAC 動態許可權、多租戶、資料許可權、工作流、三方登入、支付、簡訊、商城等功能
  • 專案地址:https://github.com/YunaiV/yudao-cloud
  • 影片教程:https://doc.iocoder.cn/video/

三、小結

本文主要圍繞在 SpringBoot 中如何引入責任鏈設計模式,介紹了三種玩法,其中第二種用法最多,其次就是第一種,第三種用的比較少,第三種本質是一種鏈式寫法,可能理解上不如第一種直觀,但是效果是一樣的。
有效的使用責任鏈設計模式,可以顯著降低業務程式碼的複雜度,可讀性更好,更容易擴充套件,希望對大家有幫助!

歡迎加入我的知識星球,全面提升技術能力。
👉 加入方式,長按”或“掃描”下方二維碼噢
星球的內容包括:專案實戰、面試招聘、原始碼解析、學習路線。

文章有幫助的話,在看,轉發吧。
謝謝支援喲 (*^__^*)

相關文章