Spring AOP 實現(xiàn)“切面式”valid校驗

why:
為什么要用aop實現(xiàn)校驗慨亲?
answer:
spring mvc 默認自帶的校驗機制 @Valid + BindingResult,
但這種默認實現(xiàn)都得在Controller方法的中去接收BindingResult副女,從而進行校驗.
eg:

if (result.hasErrors()) {
  List<ObjectError> allErrors = result.getAllErrors();
  List<String> errorlists = new ArrayList<>();
    for (ObjectError objectError : allErrors) {
        errorlists.add(objectError.getDefaultMessage());
    }
 }

獲取errorlists臂港。這樣實現(xiàn)的話,每個需要校驗的方法都得重復調(diào)用泉沾,即使封裝也是上岗。

可能上面那么說還不能表明spring 的@Valid + BindingResult實現(xiàn)凄鼻,我先舉個“栗子”随橘。

1. 栗子(舊版本)

1.1 接口層(IDAL)

eg: 簡單的POST請求,@RequestBody接收請求數(shù)據(jù)锦庸,@Valid + BindingResult進行校驗

  • httpMethid: POST
  • parameters:@RequestBody接收請求數(shù)據(jù)
  • valid:@Valid +BindingResult
 @ResponseBody
 @PostMapping("body")
 public ResponseVO bodyPost(@RequestBody @Valid TestVO body,BindingResult result){
   //校驗到錯誤
   if (result.hasErrors()) {
      List<ObjectError> allErrors = result.getAllErrors();
      List<String> lists = new ArrayList<>();
      for (ObjectError objectError : allErrors) {
          lists.add(objectError.getDefaultMessage());
      }
      return new ResponseVO(HttpStatus.BAD_REQUEST.value(), "parameter empty", lists);
  }
     return new ResponseVO(HttpStatus.OK.value(), "bodyPost", null);
}

1.2 實體(vo)校驗內(nèi)容

@Valid + BindingResult的校驗注解一大堆机蔗,網(wǎng)上一摸就有的!

public class TestVO {
    @Getter
    @Setter
    @Min(value = 0,message = "請求參數(shù)isString不能小于0")
    private Integer isInt;
    @Getter
    @Setter
    @NotBlank(message = "請求參數(shù)isString不能為空")
    private String isString;
}

1.3 結(jié)果測試

測試結(jié)果1

2. aop校驗(升級版)

可以看到若是多個像bodyPost一樣都需要對body進行校驗的話,那么有一坨代碼就必須不斷復現(xiàn)萝嘁,即使改為父類可復用方法梆掸,也得去調(diào)用。所以左思右想還是覺得不優(yōu)雅牙言。所以有了aop進行切面校驗酸钦。

2.1 接口層(IDAL)

是的!你沒看錯咱枉,上面那一坨代碼沒了卑硫,也不需要調(diào)用父類的的共用方法。就單單一個注解就完事了:@ParamValid

@ParamValid
@ResponseBody
@PostMapping("body")
public ResponseVO bodyPost(@RequestBody @Valid TestVO body,BindingResult result){
    return new ResponseVO("bodyPost", null);
}

2.2 自定義注解(annotation)

這個注解也是簡簡單單的用于方法的注解蚕断。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ParamValid {}

2.3 重點欢伏!切面實現(xiàn)(Aspect)

切面詳解:

  • @Before: 使用注解方式@annotation(XX),凡是使用到所需切的注解(@ParamValid),都會調(diào)用該方法
  • JoinPoint: 通過JoinPoint獲取方法的參數(shù)亿乳,以此獲取BindingResult所校驗到的內(nèi)容
  • 遷移校驗封裝: 將原先那一坨校驗遷移到Aspect中:validRequestParams
  • 響應校驗結(jié)果:
    • 通過RequestContextHolder獲取response
    • 獲取響應OutputStream
    • 將BindingResult封裝響應
@Aspect
@Component
public class ParamValidAspect {

    private static final Logger log = LoggerFactory.getLogger(ParamValidAspect.class);

    @Before("@annotation(paramValid)")
    public void paramValid(JoinPoint point, ParamValid paramValid) {
        Object[] paramObj = point.getArgs();
        if (paramObj.length > 0) {
            if (paramObj[1] instanceof BindingResult) {
                BindingResult result = (BindingResult) paramObj[1];
                ResponseVO errorMap = this.validRequestParams(result);
                if (errorMap != null) {
                    ServletRequestAttributes res = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                    HttpServletResponse response = res.getResponse();
                    response.setCharacterEncoding("UTF-8");
                    response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                    response.setStatus(HttpStatus.BAD_REQUEST.value());

                    OutputStream output = null;
                    try {
                        output = response.getOutputStream();
                        errorMap.setCode(null);
                        String error = new Gson().toJson(errorMap);
                        log.info("aop 檢測到參數(shù)不規(guī)范" + error);
                        output.write(error.getBytes("UTF-8"));
                    } catch (IOException e) {
                        log.error(e.getMessage());
                    } finally {
                        try {
                            if (output != null) {
                                output.close();
                            }
                        } catch (IOException e) {
                            log.error(e.getMessage());
                        }
                    }
                }
            }
        }
    }

    /**
     * 校驗
     */
    private ResponseVO validRequestParams(BindingResult result) {
        if (result.hasErrors()) {
            List<ObjectError> allErrors = result.getAllErrors();
            List<String> lists = new ArrayList<>();
            for (ObjectError objectError : allErrors) {
                lists.add(objectError.getDefaultMessage());
            }
            return new ResponseVO(HttpStatus.BAD_REQUEST.value(), "parameter empty", lists);
        }
        return null;
    }
}

2.4 測試結(jié)果

測試結(jié)果2

看了上面兩種結(jié)果硝拧,就可以對比出使用Spring AOP 配合@Valid + BindingResult進行校驗的優(yōu)點:

  • 去除代碼冗余
  • AOP異步處理
  • 優(yōu)化代碼實現(xiàn)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市葛假,隨后出現(xiàn)的幾起案子障陶,更是在濱河造成了極大的恐慌,老刑警劉巖聊训,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抱究,死亡現(xiàn)場離奇詭異,居然都是意外死亡魔眨,警方通過查閱死者的電腦和手機媳维,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來遏暴,“玉大人侄刽,你說我怎么就攤上這事∨罅梗” “怎么了州丹?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長杂彭。 經(jīng)常有香客問我墓毒,道長,這世上最難降的妖魔是什么亲怠? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任所计,我火速辦了婚禮,結(jié)果婚禮上团秽,老公的妹妹穿的比我還像新娘主胧。我一直安慰自己叭首,他們只是感情好,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布踪栋。 她就那樣靜靜地躺著焙格,像睡著了一般。 火紅的嫁衣襯著肌膚如雪夷都。 梳的紋絲不亂的頭發(fā)上眷唉,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機與錄音囤官,去河邊找鬼冬阳。 笑死,一個胖子當著我的面吹牛治拿,可吹牛的內(nèi)容都是我干的摩泪。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼劫谅,長吁一口氣:“原來是場噩夢啊……” “哼见坑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起捏检,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤荞驴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后贯城,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體熊楼,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年能犯,在試婚紗的時候發(fā)現(xiàn)自己被綠了鲫骗。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡踩晶,死狀恐怖执泰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情渡蜻,我是刑警寧澤术吝,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站茸苇,受9級特大地震影響排苍,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜学密,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一淘衙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧腻暮,春花似錦幔翰、人聲如沸漩氨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至款青,卻和暖如春做修,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背抡草。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工饰及, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人康震。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓燎含,卻偏偏與公主長得像,于是被迫代替她去往敵國和親腿短。 傳聞我的和親對象是個殘疾皇子屏箍,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容