基于Hibernate Validate的后端驗證

基于AOP和Hibernate Validate框架的后臺驗證

整體思路
  • 對Controller層的方法做切面攔截
  • 驗證入參是否使用@DoValid 注解修飾
  • 在切面類中調用Hibernate Validate框架依次對入參進行驗證
  • 全部驗證合法,執(zhí)行原方法體击吱,若驗證不通過淋淀,動態(tài)判斷返回類型,并構造返回結果
Demo
  • 新增
  • 查詢
常用注解
Annotation Value Scope
@NotNull 不為null 字段或屬性
@NotEmpty 不為null同時也不為空 字段或屬性,String,Collection,Map,數組
@NotBlank 字符串不為null,并且不是空字符串(忽略前后空白字符) 字段或屬性
@Pattern 是否匹配正則 String
... ... ...
自定義注解
  • 定義注解
  • 定義驗證類
如何與系統(tǒng)整合
  1. pom.xml添加如下依賴:
<dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>5.3.3.Final</version>
</dependency>
  1. 在spirng.xml中添加如下配置:
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">  
      <property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>  
</bean> 

或者使用@Bean注解覆醇,注入該Bean

  1. 在pom.xml中引入vb依賴或者將如下文件拷貝到對應系統(tǒng)中
    DoValid.javaDoValidAspect.java
  /*
  * Copyright (c) 2016, www.vnetoo.com. All rights reserved.
  */
 package com.vnetoo.vcomponent.validate;

 import java.lang.annotation.Documented;
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;

 /**
  * 參數驗證注解
  * @date 2016年11月25日 下午4:48:53
  * @author zhaoj
  * @since V2.0.0
  */
 @Target({ElementType.PARAMETER})
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 public @interface DoValid {
          //如需要引入分組朵纷,加入如下這句即可,代碼中使用@DoValid({GroupA.class})
          //Class<?>[] value() default {};
 }
 /*
  * Copyright (c) 2016, www.vnetoo.com. All rights reserved.
  */
 package com.vnetoo.vcomponent.validate;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Method;
 import java.util.Iterator;
 import java.util.Set;
 
 import javax.validation.ConstraintViolation;
 
 import org.apache.commons.lang.StringUtils;
 import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.annotation.Around;
 import org.aspectj.lang.annotation.Aspect;
 import org.aspectj.lang.annotation.Pointcut;
 import org.aspectj.lang.reflect.MethodSignature;
 import org.springframework.stereotype.Component;
 import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
 import org.springframework.web.bind.annotation.ResponseBody;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.servlet.ModelAndView;
 
 import com.vnetoo.common.AppContext;
 import com.vnetoo.common.enums.Result;
 import com.vnetoo.common.vo.AjaxResponse;
 import com.vnetoo.common.vo.CommonResponse;
 
 /**
  * 表單后臺驗證切面
  * 
  * @date 2016年11月25日 上午10:12:21
  * @author zhaoj
  * @since V2.0.0
  */
 @Aspect
 @Component
 public class DoValidAspect {
     /**
      * 驗證類的結尾
      * 
      * @date 2016年11月25日 下午4:51:41
      * @author zhaoj
      * @since V2.0.0
      */
     public static final String VALIDATOR_SUFFIX = "Validator";
 
     /**
      * 定義切面
      * 
      * @date 2016年11月25日 上午10:12:02
      * @author zhaoj
      * @since V2.0.0
      */
     @Pointcut("execution(public * com.vnetoo..*Controller.*(..))")
     public void validate() {
     }
 
     /**
      * 切面方法體環(huán)繞
      * 
      * @date 2016年11月25日 上午10:11:33
      * @author zhaoj
      * @since V2.0.0
      * @param pjp
      * @return
      * @throws Throwable
      */
     @Around(value = "validate()")
     private Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
         /*
          * 算法: 1.取得方法體中的參數注解二維數組 2.逐一遍歷參數中的注解永脓,并對帶有帶有 @Valid 注解的參數做驗證處理
          * 
          * 驗證處理: 1.調用當前Controller中的validate()
          * 2.如果返回的CommonResponse.Result!=Result.Success袍辞,驗證返回類型 
          * 2.1 JSON(主要用于添加、編輯):判斷Controller是否有@RestController 或者 方法上加了@ResponseBody常摧,返回帶錯誤信息的JSON
          * 2.2 View(主要用于查詢):        判斷Controller是否返回值為ModelAndView搅吁,返回帶錯誤信息的錯誤頁面
          * 
          * 其中,錯誤信息在CommonResponse.msg中
          * 3.若所有驗證都通過了落午,則可以繼續(xù)執(zhí)行方法
          */
         MethodSignature signature = (MethodSignature) pjp.getSignature();
         Method method = signature.getMethod();
 
         //[參數][注解]二維數組
         Annotation[][] parameterAnnotations = method.getParameterAnnotations();
 
         if (parameterAnnotations != null && parameterAnnotations.length != 0) {
             for (int i = 0; i != parameterAnnotations.length; ++i) {
                 Annotation[] param = parameterAnnotations[i];
                 for (int j = 0; j != param.length; ++j) {
                     Annotation annotation = param[j];
                     if (annotation instanceof DoValid) {
                         // 參數中有驗證注解
                         Object bo = pjp.getArgs()[i];
 
                         /*******************單個入參驗證開始*******************/
                         CommonResponse validator = validate(bo);
                         //分組支持
                         //CommonResponse validator = validate(bo,((DoValid) annotation).value());
                         if (validator.getResult() != Result.SUCCESS) {
                             //驗證不通過,驗證返回方式
                             if(isJsonResponse(pjp)){
                                 //返回JSON
                                 return new AjaxResponse(validator .getResult(), AppContext.token(), validator .getMsg());
                             }else{
                                 //返回View
                                 return new ModelAndView("errorMsg").addObject("errMsg", validator .getMsg());
                             }
                         }
                         /*******************單個入參驗證結束*******************/
                     }
                 }
             }
         }
         
         //所有入參驗證完畢谎懦、已做字段過濾,且均合法
         //代碼若能執(zhí)行到此處溃斋,則說明驗證通過界拦,則執(zhí)行Controller層中的方法體
         return pjp.proceed();
     }
 
     /**
      * 判斷是否是返回JSON的請求
      * @date 2016年12月1日 下午4:45:50
      * @author zhaoj
      * @since V2.0.0
      * @param pjp
      * @return
      */
     public static boolean isJsonResponse(ProceedingJoinPoint pjp){
         MethodSignature signature = (MethodSignature) pjp.getSignature();
         Method method = signature.getMethod();
         Annotation[] methodAnnotations = method.getAnnotations();
         for(Annotation tmp : methodAnnotations){
             if(tmp instanceof ResponseBody){
                 return true;
             }
         }
         
         Annotation[] classAnnotations = method.getClass().getAnnotations();
         for(Annotation tmp : classAnnotations){
             if(tmp instanceof RestController){
                 return true;
             }
         }
         
         return false;
     }
     
     /**
      * 參數驗證
      * @date 2016年12月1日 下午4:46:00
      * @author zhaoj
      * @since V2.0.0
      * @param bo
      * @return
      */
     public static CommonResponse validate(Object bo) {
         Set<ConstraintViolation<Object>> set = ((LocalValidatorFactoryBean) AppContext.getBean("validator")).getValidator().validate(bo);
         //分組驗證支持         
         //Set<ConstraintViolation<Object>> set2 = ((LocalValidatorFactoryBean) AppContext.getBean("validator")).getValidator().validate(bo, ((DoValid) annotation).value());
         if (set != null && !set.isEmpty()) {
             String msg = "";
 
             Iterator<ConstraintViolation<Object>> iterator = set.iterator();
             while (iterator.hasNext()) {
                 String singleMsg = iterator.next().getMessage();
                 if (StringUtils.isNotEmpty(singleMsg)) {
                     msg += singleMsg + ",";
                 }
             }
 
             if (StringUtils.isNotEmpty(msg.toString())) {
                 msg = msg.substring(0, msg.toString().length() - 1);
             }
 
             return new CommonResponse(Result.ERROR, msg);
         }
 
         return new CommonResponse(Result.SUCCESS, null);
     }
 }

如果需要指定分組的形式,請查看鏈接:分組驗證梗劫,然后對上述代碼略加修改即可寞奸。

  1. 不采用分組策略:建XXXCreateFormXXXEditForm在跳、XXXSearchForm類并在From的屬性上加驗證注解即可
    采用分組策略:建立分組接口枪萄,在BO的屬性上加驗證注解,并指定分組猫妙,詳見:分組驗證
  2. 在需要驗證的入參中加上@DoValid注解修飾即可瓷翻,如:
 /**
  * 添加保存
  * @date 2016-12-09 15:19:51
  * @author zhaoj
  * @since V1.0.3
  * @param bo
  * @return
  */
 @CheckToken
 @ResponseBody
 @Authorization
 @LogMark(memo="添加保存")
 @RequestMapping(value = "/insert")
 public AjaxResponse insert(@DoValid ExamUserCreateForm form){
     try {
         ExamUser bo = new ExamUser();
         PropertyUtils.copyProperties(bo, form);
         getService().insert(bo);
         
         return new AjaxResponse();
     } catch (Exception e) {
         return new AjaxResponse(Result.ERROR, AppContext.token(), e.getMessage());
     }
 }
參考

Hibernate Validate 中文 API
Hibernate Validate 常用注解

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市割坠,隨后出現的幾起案子齐帚,更是在濱河造成了極大的恐慌,老刑警劉巖彼哼,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件对妄,死亡現場離奇詭異,居然都是意外死亡敢朱,警方通過查閱死者的電腦和手機剪菱,發(fā)現死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進店門摩瞎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人孝常,你說我怎么就攤上這事旗们。” “怎么了构灸?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵上渴,是天一觀的道長。 經常有香客問我喜颁,道長稠氮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任半开,我火速辦了婚禮括袒,結果婚禮上,老公的妹妹穿的比我還像新娘稿茉。我一直安慰自己锹锰,他們只是感情好,可當我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布漓库。 她就那樣靜靜地躺著恃慧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪渺蒿。 梳的紋絲不亂的頭發(fā)上痢士,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天,我揣著相機與錄音茂装,去河邊找鬼怠蹂。 笑死,一個胖子當著我的面吹牛少态,可吹牛的內容都是我干的城侧。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼彼妻,長吁一口氣:“原來是場噩夢啊……” “哼嫌佑!你這毒婦竟也來了?” 一聲冷哼從身側響起侨歉,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤屋摇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后幽邓,有當地人在樹林里發(fā)現了一具尸體炮温,經...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年牵舵,在試婚紗的時候發(fā)現自己被綠了柒啤。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片倦挂。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖白修,靈堂內的尸體忽然破棺而出妒峦,到底是詐尸還是另有隱情重斑,我是刑警寧澤兵睛,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站窥浪,受9級特大地震影響祖很,放射性物質發(fā)生泄漏。R本人自食惡果不足惜漾脂,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一假颇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧骨稿,春花似錦笨鸡、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至辙浑,卻和暖如春激涤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背判呕。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工倦踢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人侠草。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓辱挥,卻偏偏與公主長得像,于是被迫代替她去往敵國和親边涕。 傳聞我的和親對象是個殘疾皇子般贼,可洞房花燭夜當晚...
    茶點故事閱讀 44,629評論 2 354

推薦閱讀更多精彩內容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現奥吩,斷路器哼蛆,智...
    卡卡羅2017閱讀 134,652評論 18 139
  • 什么是注解(Annotation):Annotation(注解)就是Java提供了一種元程序中的元素關聯(lián)任何信息和...
    九尾喵的薛定諤閱讀 3,163評論 0 2
  • hibernate Validator 是 Bean Validation 的參考實現 。 Hibernate V...
    一路逆風i閱讀 3,087評論 0 1
  • Java 8自Java 5(發(fā)行于2004)以來最具革命性的版本霞赫。Java 8 為Java語言腮介、編譯器、類庫端衰、開發(fā)...
    誰在烽煙彼岸閱讀 888評論 0 4
  • 說謊有時候是為了保護別人叠洗、為了讓對方喜歡自己甘改、為了博取同情、為了逃避灭抑,可有的人一生愛說謊十艾,就是喜歡說謊。開始了說謊...
    小樣兒的小小樣兒927閱讀 1,543評論 0 0