日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

Java業務校驗工具實現方法

瀏覽:204日期:2022-08-31 08:04:11

一、背景

在我們日常接口開發過程中,可能要面對一些稍微復雜一些的業務邏輯代碼的編寫,在執行真正的業務邏輯前,往往要進行一系列的前期校驗工作,校驗可以分為參數合法性校驗和業務數據校驗。

參數合法性校驗比如最常見的校驗參數值非空校驗、格式校驗、最大值最小值校驗等,可以通過Hibernate Validator框架實現,本文不具體講解。業務數據校驗通常與實際業務相關,比如提交訂單接口,我們可能需要校驗商品是否合法、庫存是否足夠、客戶余額是否足夠、還有其他的一些風控校驗。我們的代碼可能看起來像是這樣的:

public ApiResult<OrderSubmitVo> submitOrder(OrderSubmitDto orderSubmitDto) { // 業務校驗1 // 業務校驗2 // 業務校驗3 // 業務校驗n... // 執行真正的業務邏輯 return ApiResult.success();}

二、問題

實現不夠優雅

上述代碼在版本迭代的過程中,還可能陸陸續續增加/修改一些校驗邏輯,如果業務邏輯校驗的代碼都耦合在核心業務邏輯中,這樣實現其實是不夠優雅,不符合設計原則的單一職責原則和開閉原則。

校驗代碼無法復用

如果某個業務校驗代碼需要在其他業務中也會用到,那我們則需要將相同的代碼復制一份至業務代碼中,比如校驗用戶狀態,在很多業務校驗中都需要校驗,如果校驗邏輯有些許更改的話,那么所有涉及到的地方都要同步修改,這樣不利于系統維護。

校驗邏輯無法按照順序依賴執行,并且校驗過程中產生的數據后續獲取不便

如果我們將上述代碼中的各個校驗邏輯封裝成獨立的子方法,那有可能存在業務校驗2要依賴于業務校驗1的數據結果,并且在業務校驗過程中產生的數據在后續執行真正的業務邏輯的時候是需要用得到的。

三、校驗工具實現思路

我們要寫的校驗工具至少要解決上面所說的三個問題

業務校驗代碼與核心業務邏輯代碼解耦 同一個校驗器可以用于多個業務,提高代碼的復用性和可維護性 校驗代碼可以按照指定順序執行,并且校驗過程中產生的數據可以后續傳遞

在用zuul來做網關服務的時候,我獲得了一些靈感,zuul中的filterType用來區分請求路由到目標之前、處理目標請求、目標請求返回后的類型,filterOrder用來指定過濾器的執行順序,RequestContext為請求上下文,RequestContext繼承自ConcurrentHashMap,且與ThreadLocal綁定保證線程安全,請求上下文中的數據在一次請求的所有過濾器中可以獲取,很好的完成了數據傳遞。

首先我們需要定義一個校驗器注解,注解中指定業務類型和執行順序,在校驗器上加上該注解表明這是一個校驗器。定義一個校驗器上下文,在業務校驗執行過程中產生的數據可以通過上下文進行傳遞。定義一個校驗器基類,校驗器繼承基類,并實現其中的具體校驗方法。定義一個校驗器的統一執行器,執行器可以根據業務類型找出所有帶有校驗器注解并且是指定業務類型的校驗器列表,根據校驗器注解中的執行順序排序后,遍歷所有校驗器列表調用校驗方法。如果校驗過程中校驗失敗,則拋出校驗異常中斷業務執行。

以上為大概的實現思路,具體的實現代碼如下:

四、show me your code

Validator.java

import java.lang.annotation.*;/** * @author: 會跳舞的機器人 * @date: 2019/10/23 13:58 * @description: 業務校驗注解 */@Documented@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public @interface Validator { /** * 業務類型,同一個校驗器可以指定多個業務類型 * * @return */ String[] validateTypes(); /** * 執行順序,數值越小越先執行 * * @return */ int validateOrder();}

Validator校驗注解,在校驗器的類上加上該注解則表明為業務校驗器,validateTypes表示業務類型,同一個校驗器可以指定多個業務類型,多個業務類型可以復用同一個校驗器,validateOrder表示執行順序,數值越小越先被執行。

ValidatorContext.java

import java.util.concurrent.ConcurrentHashMap;/** * @author: 會跳舞的機器人 * @date: 2019/9/11 14:56 * @description: 校驗器上下文,與當前線程綁定 */public class ValidatorContext extends ConcurrentHashMap<String, Object> { /** * 請求對象 */ public Object requestDto; protected static final ThreadLocal<? extends ValidatorContext> threadLocal = ThreadLocal.withInitial(() -> new ValidatorContext()); /** * 獲取當前線程的上下文 * * @return */ public static ValidatorContext getCurrentContext() { ValidatorContext context = threadLocal.get(); return context; } /** * 設值 * * @param key * @param value */ public void set(String key, Object value) { if (value != null) put(key, value); else remove(key); } /** * 獲取String值 * * @param key * @return */ public String getString(String key) { return (String) get(key); } /** * 獲取Integer值 * * @param key * @return */ public Integer getInteger(String key) { return (Integer) get(key); } /** * 獲取Boolean值 * * @param key * @return */ public Boolean getBoolean(String key) { return (Boolean) get(key); } /** * 獲取對象 * * @param key * @param <T> * @return */ public <T> T getClazz(String key) { return (T) get(key); } /** * 獲取Long值 * * @param key * @return */ public Long getLong(String key) { return (Long) get(key); } public <T> T getRequestDto() { return (T) requestDto; } public void setRequestDto(Object requestDto) { this.requestDto = requestDto; }

ValidatorContext為請求上下文,與當前請求線程綁定,繼承自ConcurrentHashMap,requestDto屬性為接口請求入參對象,提供get/set方法使得在上下文中能更加便捷的獲取請求入參數據。

ValidatorTemplate.java

/** * @author: 會跳舞的機器人 * @date: 2019/10/23 11:51 * @description: 校驗器模板,業務校驗器需繼承模板類 */@Slf4j@Componentpublic abstract class ValidatorTemplate { /** * 校驗方法 */ public void validate() { try { validateInner(); } catch (ValidateException e) { log.error('業務校驗失敗', e); throw e; } catch (Exception e) { log.error('業務校驗異常', e); ValidateException validateException = new ValidateException(ResultEnum.VALIDATE_ERROR); throw validateException; } } /** * 校驗方法,由子類具體實現 * * @throws ValidateException */ protected abstract void validateInner() throws ValidateException;}

校驗器抽象類,具體的校驗器需要繼承該類,并且實現具體的validateInner校驗方法。

ValidatorTemplateProxy.java

/** * @author: 會跳舞的機器人 * @date: 2019/10/25 18:03 * @description: ValidatorTemplate代理類 */@Data@AllArgsConstructorpublic class ValidatorTemplateProxy extends ValidatorTemplate implements Comparable<ValidatorTemplateProxy> { private ValidatorTemplate validatorTemplate; private String validateType; private int validateOrder; @Override public int compareTo(ValidatorTemplateProxy o) { return Integer.compare(this.getValidateOrder(), o.getValidateOrder()); } @Override protected void validateInner() throws ValidateException { validatorTemplate.validateInner(); }}

ValidatorTemplate類的代理類,實現了Comparable排序接口,便于校驗器按照validateOrder屬性排序,并且將校驗器中的注解轉化為代理類中的兩個屬性字段,方便執行過程中的統一日志打印。

ValidateProcessor.java

import java.lang.annotation.Annotation;import java.util.*;/** * @author: 會跳舞的機器人 * @date: 2019/10/25 18:02 * @description: 執行器 */@Slf4j@Componentpublic class ValidateProcessor { /** * 執行業務類型對應的校驗器 * * @param validateType */ public void validate(String validateType) { if (StringUtils.isEmpty(validateType)) { throw new IllegalArgumentException('validateType cannot be null'); } long start = System.currentTimeMillis(); log.info('start validate,validateType={},ValidatorContext={}', validateType, ValidatorContext.getCurrentContext().toString()); List<ValidatorTemplateProxy> validatorList = getValidatorList(validateType); if (CollectionUtils.isEmpty(validatorList)) { log.info('validatorList is empty'); return; } ValidatorTemplateProxy validateProcessorProxy; for (ValidatorTemplateProxy validatorTemplate : validatorList) { validateProcessorProxy = validatorTemplate; log.info('{} is running', validateProcessorProxy.getValidatorTemplate().getClass().getSimpleName()); validatorTemplate.validate(); } log.info('end validate,validateType={},ValidatorContext={},time consuming {} ms', validateType,ValidatorContext.getCurrentContext().toString(), (System.currentTimeMillis() - start)); } /** * 根據Validator注解的validateType獲取所有帶有該注解的校驗器 * * @param validateType * @return */ private List<ValidatorTemplateProxy> getValidatorList(String validateType) { List<ValidatorTemplateProxy> validatorTemplateList = new LinkedList<>(); Map<String, Object> map = SpringUtil.getApplicationContext().getBeansWithAnnotation(Validator.class); String[] validateTypes; int validateOrder; Annotation annotation; for (Map.Entry<String, Object> item : map.entrySet()) { annotation = item.getValue().getClass().getAnnotation(Validator.class); validateTypes = ((Validator) annotation).validateTypes(); validateOrder = ((Validator) annotation).validateOrder(); if (item.getValue() instanceof ValidatorTemplate) {if (Arrays.asList(validateTypes).contains(validateType)) { validatorTemplateList.add(new ValidatorTemplateProxy((ValidatorTemplate) item.getValue(), validateType, validateOrder));} } else {log.info('{}not extend from ValidatorTemplate', item.getKey()); } } Collections.sort(validatorTemplateList); return validatorTemplateList; }}

業務校驗的執行器,getValidatorList方法根據validateType值獲取所有帶有該validateType值的校驗器,并將其封裝成ValidatorTemplateProxy代理類,然后再做排序。validate為統一的業務校驗方法。

ValidateException.java

/** * @author: 會跳舞的機器人 * @date: 2019/4/4 6:34 PM * @description: 校驗異常 */public class ValidateException extends RuntimeException { // 異常碼 private Integer code; public ValidateException() { } public ValidateException(String message) { super(message); } public ValidateException(ResultEnum resultEnum) { super(resultEnum.getMsg()); this.code = resultEnum.getCode(); } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; }}

ValidateException為校驗失敗時,拋出的業務校驗異常類。

ValidateTypeConstant.java

/** * @author: 會跳舞的機器人 * @date: 2019/10/30 15:16 * @description: */public class ValidateTypeConstant { /** * 提交訂單校驗 */ public static final String ORDER_SUBMIT = 'order_submit';}

ValidateTypeConstant為定義validateType業務校驗類型的常量類。

五、使用樣例

以訂單提交為例,我們首先定義了兩個個基本的校驗器,下單商品信息校驗器、客戶狀態校驗器,均為偽代碼實現。

OrderSubmitProductValidator.java

/** * @author: 會跳舞的機器人 * @date: 2019/10/30 15:34 * @description: 商品狀態以及庫存校驗 */@Component@Slf4j@Validator(validateTypes = ValidateTypeConstant.ORDER_SUBMIT, validateOrder = 1)public class OrderSubmitProductValidator extends ValidatorTemplate { @Override protected void validateInner() throws ValidateException { ValidatorContext validatorContext = ValidatorContext.getCurrentContext(); OrderSubmitDto orderSubmitDto = validatorContext.getRequestDto(); // 獲取商品信息并校驗商品狀態 List<ProductShelfVo> productShelfVoList = new ArrayList<>(); if (0 == 1) { throw new ValidateException('商品已下架'); } // 將商品信息設置至上下文中 validatorContext.set('productShelfVoList', productShelfVoList); }}

OrderSubmitCustomerValidator.java

/** * @author: 會跳舞的機器人 * @date: 2019/10/30 19:24 * @description: */@Component@Slf4j@Validator(validateTypes = ValidateTypeConstant.ORDER_SUBMIT, validateOrder = 2)public class OrderSubmitCustomerValidator extends ValidatorTemplate { @Override protected void validateInner() throws ValidateException { ValidatorContext validatorContext = ValidatorContext.getCurrentContext(); String customerNo = validatorContext.getString('customerNo'); if (StringUtils.isEmpty(customerNo)) { throw new IllegalArgumentException('客戶編號為空'); } // 獲取客戶信息并校驗客戶狀態 CustomerVo customer = new CustomerVo(); if (0 == 1) { throw new ValidateException('客戶限制交易'); } }}

在提交訂單的業務邏輯的代碼中使用:

/** * 提交訂單 * * @param orderSubmitDto * @return */public ApiResult<OrderSubmitVo> submitOrder(OrderSubmitDto orderSubmitDto) { // 業務校驗 ValidatorContext validatorContext = ValidatorContext.getCurrentContext(); validatorContext.setRequestDto(orderSubmitDto); validateProcessor.validate(ValidateTypeConstant.ORDER_SUBMIT); // 從上下文中獲取下單商品信息 List<ProductShelfVo> productShelfVoList = validatorContext.getClazz('productShelfVoList'); // 后續業務邏輯處理 return ApiResult.success();}

通過使用上述封裝的校驗工具后,業務代碼與校驗代碼解耦,后續要增加/修改業務校驗邏輯時候,我們只需要增加/修改相應的校驗器即可,不必改動到主業務邏輯。為了我們能更簡單和方便找到某個業務邏輯對應所有的校驗器,我們在命名校驗器的時候可以加上業務類型的前綴。

六、總結

1、在開發過程中,我們遇到一些“煩人”問題的時候,要想辦法解決它,而不是忽略不管它,通過解決問題可以提高我們的技術能力。

2、要善于從其他優秀的技術框架學習其實現思路。

3、以上校驗工具只是一個簡單實現,解決的問題只是筆者在開發過程中遇到的問題,可能并不一定具有通用性。

到此這篇關于Java業務校驗工具實現方法的文章就介紹到這了,更多相關Java 業務校驗內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!

標簽: Java
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
九九综合在线| 欧美日韩99| 日韩精品永久网址| 久久久久免费| 蜜桃国内精品久久久久软件9| 亚洲电影在线| 日韩在线黄色| 国产精品九九| 蜜臀国产一区| 亚洲在线网站| 国产精品分类| 99久久夜色精品国产亚洲1000部| 91精品观看| 一区二区国产在线观看| 欧美亚洲国产日韩| av中文资源在线资源免费观看| 亚洲午夜在线| 日本aⅴ免费视频一区二区三区| 免费日韩一区二区三区| 成人看片网站| 婷婷精品久久久久久久久久不卡| 久久精品国内一区二区三区| 免费毛片在线不卡| 日韩av一二三| 亚洲成a人片| 欧美日韩午夜电影网| 蜜桃av.网站在线观看| 在线国产精品一区| 国产精品久久久久蜜臀| 亚洲免费毛片| 91av亚洲| 日韩av一二三| 亚洲精品一二三区区别| 国产精品久久久久久久久久齐齐| 一本大道色婷婷在线| 日韩精品久久久久久久软件91| 精品一区视频| 综合一区av| 日韩成人亚洲| 国产精品永久| 爽好久久久欧美精品| 麻豆理论在线观看| 国产日产一区| 精品久久久久中文字幕小说| 久久黄色影视| 一本一道久久a久久精品蜜桃| 五月天久久网站| 国产精品手机在线播放| 日韩精品看片| 欧美久久亚洲| 91久久亚洲| 久久免费福利| 国产日韩欧美三区| 激情五月综合| 97久久亚洲| 欧美成人高清| 裤袜国产欧美精品一区| 亚洲精品乱码久久久久久蜜桃麻豆| 成午夜精品一区二区三区软件| 在线精品一区二区| 91精品亚洲| 国产成人黄色| 国产精品视频首页| 91精品国产自产观看在线| 精品一区免费| 快播电影网址老女人久久| 久久精品伊人| 国产精品porn| 69精品国产久热在线观看| 久久亚洲国产精品一区二区| 国产成人调教视频在线观看| 91精品丝袜国产高跟在线| 中文字幕av亚洲精品一部二部| 国产va在线视频| 美女在线视频一区| 亚洲aa在线| 在线亚洲欧美| 亚洲二区免费| 亚洲福利专区| 日韩在线不卡| 九九色在线视频| 亚洲综合电影| 91欧美国产| 最新中文字幕在线播放 | 国产日韩综合| 亚洲大全视频| 欧美福利一区| 在线精品视频在线观看高清| 99久久www免费| 国产一区亚洲| 亚洲综合中文| 欧美久久亚洲| 免费亚洲婷婷| 伊人网在线播放| 日韩三区免费| 不卡一区综合视频| 三级欧美在线一区| 日韩二区三区在线观看| 国产精品亚洲综合色区韩国| 久久男人av| 日韩一区二区中文| 夜夜嗨一区二区三区| 蜜桃视频一区二区三区在线观看| 亚洲一二三区视频| 91亚洲精品视频在线观看| 久久国产精品美女| 成人国产精品| 美女精品在线观看| 国产日韩欧美中文在线| 久久精品国产999大香线蕉| 精品欧美视频| 欧美va天堂在线| 亚洲精品乱码久久久久久蜜桃麻豆| 欧美一区免费| 成人精品久久| 国产视频一区欧美| 欧美伊人影院| 欧美日韩在线观看首页| 黄色成人91| 国产日韩一区二区三区在线| 国产一区二区三区不卡视频网站 | 91精品1区| 婷婷视频一区二区三区| 国产精品男女| 久久久久久久久久久9不雅视频| 久久不射中文字幕| 国产日产精品_国产精品毛片| 麻豆精品视频在线观看| av高清一区| 日本中文字幕一区二区| 国产在视频一区二区三区吞精| 久久天堂精品| 亚洲精品视频一二三区| 日韩av二区| 亚洲精品三级| 青青久久av| 国产探花一区| 自由日本语亚洲人高潮| 欧美亚洲一区二区三区| 久久视频一区| 国产精品久久久亚洲一区| 午夜久久黄色| 国产一区一一区高清不卡| 日韩在线播放一区二区| 日韩综合一区| 日韩精品午夜视频| 亚洲91精品| 国产欧美一区二区三区精品观看| 久久男女视频| 精品视频一区二区三区四区五区| 日韩中文字幕区一区有砖一区| 精品91福利视频| 日韩高清电影一区| 一区二区小说| 高清不卡亚洲| 欧美aaaaaa午夜精品| 亚洲午夜久久| 欧美综合另类| 成人亚洲欧美| 久久99国产精品视频| 性色一区二区| 亚洲手机视频| 美女福利一区二区三区| 国产精品亚洲欧美日韩一区在线| 亚洲一区黄色| 99久久久久| 日韩av在线中文字幕| 欧美日韩调教| 日韩精品a在线观看91| 欧美综合国产| 激情婷婷亚洲| 日韩欧美精品一区| 精品免费视频| 国产精品v亚洲精品v日韩精品| 亚洲精品麻豆| 免费国产亚洲视频| 99pao成人国产永久免费视频| 欧美一区二区三区高清视频| 国产一区2区| 国产精品99视频| 欧美激情日韩| 麻豆精品av| 国产精品videossex| 国产精品亚洲四区在线观看| 中文字幕av一区二区三区人 | 日韩av在线免费观看不卡| 在线国产精品一区| 亚洲一区二区三区四区五区午夜 | 国产精品久久久久77777丨| 日本成人中文字幕| 日韩福利在线观看| 欧美一级二区| 国产精品白浆| 你懂的国产精品| 免费视频一区二区三区在线观看 | av免费不卡国产观看| 国产va在线视频| 久久天堂成人| 另类激情亚洲| 91p九色成人|