1. 程式人生 > >Spring4新特性——整合Bean Validation 1.1(JSR-349)到SpringMVC

Spring4新特性——整合Bean Validation 1.1(JSR-349)到SpringMVC

Bean Validation 1.1當前實現是Hibernate validator 5,且spring4才支援。接下來我們從以下幾個方法講解Bean Validation 1.1,當然不一定是新特性:

  1.  整合Bean Validation 1.1到SpringMVC
  2.  分組驗證、分組順序及級聯驗證
  3.  訊息中使用EL表示式
  4.  方法引數/返回值驗證
  5.  自定義驗證規則
  6.  類級別驗證器
  7.  指令碼驗證器
  8.  cross-parameter,跨引數驗證
  9. 混合類級別驗證器和跨引數驗證器
  10. 組合多個驗證註解
  11. 本地化

因為大多數時候驗證都配合web框架使用,而且很多朋友都諮詢過如分組/跨引數驗證,所以本文介紹下這些,且是和SpringMVC框架整合的例子,其他使用方式(比如整合到JPA中)可以參考其官方文件:

 1、整合Bean Validation 1.1到SpringMVC

1.1、專案搭建

首先新增hibernate validator 5依賴:

Java程式碼  收藏程式碼
  1. <dependency>  
  2.     <groupId>org.hibernate</groupId>  
  3.     <artifactId>hibernate-validator</artifactId>  
  4.     <version>5.0.2.Final</version>  
  5. </dependency>  

如果想在訊息中使用EL表示式,請確保EL表示式版本是 2.2或以上,如使用Tomcat6,請到Tomcat7中拷貝相應的EL jar包到Tomcat6中。

Java程式碼  收藏程式碼
  1. <dependency>  
  2.     <groupId>javax.el</groupId>  
  3.     <artifactId>javax.el-api</artifactId>  
  4.     <version>2.2.4</version>  
  5.     <scope>provided</scope>  
  6. </dependency>  

請確保您使用的Web容器有相應版本的el jar包。

對於其他POM依賴請下載附件中的專案參考。

1.2、Spring MVC配置檔案(spring-mvc.xml):

Java程式碼  收藏程式碼
  1. <!-- 指定自己定義的validator -->  
  2. <mvc:annotation-driven validator="validator"/>  
  3. <!-- 以下 validator  ConversionService 在使用 mvc:annotation-driven 會 自動註冊-->  
  4. <bean id="validator"class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">  
  5.     <property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>  
  6.     <!-- 如果不加預設到 使用classpath下的 ValidationMessages.properties -->  
  7.     <property name="validationMessageSource" ref="messageSource"/>  
  8. </bean>  
  9. <!-- 國際化的訊息資原始檔(本系統中主要用於顯示/錯誤訊息定製) -->  
  10. <bean id="messageSource"class="org.springframework.context.support.ReloadableResourceBundleMessageSource">  
  11.     <property name="basenames">  
  12.         <list>  
  13.             <!-- 在web環境中一定要定位到classpath 否則預設到當前web應用下找  -->  
  14.             <value>classpath:messages</value>  
  15.             <value>classpath:org/hibernate/validator/ValidationMessages</value>  
  16.         </list>  
  17.     </property>  
  18.     <property name="useCodeAsDefaultMessage" value="false"/>  
  19.     <property name="defaultEncoding" value="UTF-8"/>  
  20.     <property name="cacheSeconds" value="60"/>  
  21. </bean>  

此處主要把bean validation的訊息查詢委託給spring的messageSource。

1.3、實體驗證註解:

Java程式碼  收藏程式碼
  1. publicclass User implements Serializable {  
  2.     @NotNull(message = "{user.id.null}")  
  3.     private Long id;  
  4.     @NotEmpty(message = "{user.name.null}")  
  5.     @Length(min = 5, max = 20, message = "{user.name.length.illegal}")  
  6.     @Pattern(regexp = "[a-zA-Z]{5,20}", message = "{user.name.illegal}")  
  7.     private String name;  
  8.     @NotNull(message = "{user.password.null}")  
  9.     private String password;  
  10. }  

1.4、錯誤訊息檔案messages.properties:

Java程式碼  收藏程式碼
  1. user.id.null=使用者編號不能為空  
  2. user.name.null=使用者名稱不能為空  
  3. user.name.length.illegal=使用者名稱長度必須在520之間  
  4. user.name.illegal=使用者名稱必須是字母  
  5. user.password.null=密碼不能為空  

1.5、控制器

Java程式碼  收藏程式碼
  1. @Controller
  2. publicclass UserController {  
  3.     @RequestMapping("/save")  
  4.     public String save(@Valid User user, BindingResult result) {  
  5.         if(result.hasErrors()) {  
  6.             return"error";  
  7.         }  
  8.         return"success";  
  9.     }  
  10. }  

1.6、錯誤頁面:

Java程式碼  收藏程式碼
  1. <spring:hasBindErrors name="user">  
  2.     <c:if test="${errors.fieldErrorCount > 0}">  
  3.         欄位錯誤:<br/>  
  4.         <c:forEach items="${errors.fieldErrors}" var="error">  
  5.             <spring:message var="message" code="${error.code}" arguments="${error.arguments}" text="${error.defaultMessage}"/>  
  6.             ${error.field}------${message}<br/>  
  7.         </c:forEach>  
  8.     </c:if>  
  9.     <c:if test="${errors.globalErrorCount > 0}">  
  10.         全域性錯誤:<br/>  
  11.         <c:forEach items="${errors.globalErrors}" var="error">  
  12.             <spring:message var="message" code="${error.code}" arguments="${error.arguments}" text="${error.defaultMessage}"/>  
  13.             <c:if test="${not empty message}">  
  14.                 ${message}<br/>  
  15.             </c:if>  
  16.         </c:forEach>  
  17.     </c:if>  
  18. </spring:hasBindErrors>  

大家以後可以根據這個做通用的錯誤訊息顯示規則。比如我前端頁面使用validationEngine顯示錯誤訊息,那麼我可以定義一個tag來通用化錯誤訊息的顯示:showFieldError.tag。  

1.7、測試

Java程式碼  收藏程式碼
  1. name------使用者名稱必須是字母  
  2. name------使用者名稱長度必須在520之間  
  3. password------密碼不能為空  
  4. id------使用者編號不能為空  

基本的整合就完成了。

如上測試有幾個小問題:

1、錯誤訊息順序,大家可以看到name的錯誤訊息順序不是按照書寫順序的,即不確定;

2、我想顯示如:使用者名稱【zhangsan】必須在5到20之間;其中我們想動態顯示:使用者名稱、min,max;而不是寫死了;

3、我想在修改的時候只驗證使用者名稱,其他的不驗證怎麼辦。

接下來我們挨著試試吧。

2、分組驗證及分組順序

如果我們想在新增的情況驗證id和name,而修改的情況驗證name和password,怎麼辦? 那麼就需要分組了。

首先定義分組介面:

Java程式碼  收藏程式碼
  1. publicinterface First {  
  2. }  
  3. publicinterface Second {  
  4. }  

分組介面就是兩個普通的介面,用於標識,類似於java.io.Serializable。

接著我們使用分組介面標識實體:

Java程式碼  收藏程式碼
  1. publicclass User implements Serializable {  
  2.     @NotNull(message = "{user.id.null}", groups = {First.class})  
  3.     private Long id;  
  4.     @Length(min = 5, max = 20, message = "{user.name.length.illegal}", groups = {Second.class})  
  5.     @Pattern(regexp = "[a-zA-Z]{5,20}", message = "{user.name.illegal}", groups = {Second.class})  
  6.     private String name;  
  7.     @NotNull(message = "{user.password.null}", groups = {First.class, Second.class})  
  8.     private String password;  
  9. }  

驗證時使用如:

Java程式碼  收藏程式碼
  1. @RequestMapping("/save")  
  2. public String save(@Validated({Second.class}) User user, BindingResult result) {  
  3.     if(result.hasErrors()) {  
  4.         return"error";  
  5.     }  
  6.     return"success";  
  7. }  

即通過@Validate註解標識要驗證的分組;如果要驗證兩個的話,可以這樣@Validated({First.class, Second.class})。

接下來我們來看看通過分組來指定順序;還記得之前的錯誤訊息嗎? user.name會顯示兩個錯誤訊息,而且順序不確定;如果我們先驗證一個訊息;如果不通過再驗證另一個怎麼辦?可以通過@GroupSequence指定分組驗證順序:

Java程式碼  收藏程式碼
  1. @GroupSequence({First.class, Second.class, User.class})  
  2. publicclass User implements Serializable {  
  3.     private Long id;  
  4.     @Length(min = 5, max = 20, message = "{user.name.length.illegal}", groups = {First.class})  
  5.     @Pattern(regexp = "[a-zA-Z]{5,20}", message = "{user.name.illegal}", groups = {Second.class})  
  6.     private String name;  
  7.     private String password;  
  8. }  

通過@GroupSequence指定驗證順序:先驗證First分組,如果有錯誤立即返回而不會驗證Second分組,接著如果First分組驗證通過了,那麼才去驗證Second分組,最後指定User.class表示那些沒有分組的在最後。這樣我們就可以實現按順序驗證分組了。

另一個比較常見的就是級聯驗證:

如:

Java程式碼  收藏程式碼
  1. publicclass User {  
  2.     @Valid
  3.     @ConvertGroup(from=First.class, to=Second.class)  
  4.     private Organization o;  
  5. }  

 1、級聯驗證只要在相應的欄位上加@Valid即可,會進行級聯驗證;@ConvertGroup的作用是當驗證o的分組是First時,那麼驗證o的分組是Second,即分組驗證的轉換。

3、訊息中使用EL表示式

假設我們需要顯示如:使用者名稱[NAME]長度必須在[MIN]到[MAX]之間,此處大家可以看到,我們不想把一些資料寫死,如NAME、MIN、MAX;此時我們可以使用EL表示式。

如:

Java程式碼  收藏程式碼
  1. @Length(min = 5, max = 20, message = "{user.name.length.illegal}", groups = {First.class})  

錯誤訊息:

Java程式碼  收藏程式碼
  1. user.name.length.illegal=使用者名稱長度必須在{min}到{max}之間  

其中我們可以使用{驗證註解的屬性}得到這些值;如{min}得到@Length中的min值;其他的也是類似的。

到此,我們還是無法得到出錯的那個輸入值,如name=zhangsan。此時就需要EL表示式的支援,首先確定引入EL jar包且版本正確。然後使用如:

Java程式碼  收藏程式碼
  1. user.name.length.illegal=使用者名稱[${validatedValue}]長度必須在520之間  

使用如EL表示式:${validatedValue}得到輸入的值,如zhangsan。當然我們還可以使用如${min > 1 ? '大於1' : '小於等於1'},及在EL表示式中也能拿到如@Length的min等資料。

另外我們還可以拿到一個java.util.Formatter型別的formatter變數進行格式化:

Java程式碼  收藏程式碼
  1. ${formatter.format("%04d", min)}  

4、方法引數/返回值驗證

5、自定義驗證規則

有時候預設的規則可能還不夠,有時候還需要自定義規則,比如遮蔽關鍵詞驗證是非常常見的一個功能,比如在發帖時帖子中不允許出現admin等關鍵詞。

1、定義驗證註解

Java程式碼  收藏程式碼
  1. package com.sishuok.spring4.validator;  
  2. import javax.validation.Constraint;  
  3. import javax.validation.Payload;  
  4. import java.lang.annotation.Documented;  
  5. import java.lang.annotation.Retention;  
  6. import java.lang.annotation.Target;  
  7. importstatic java.lang.annotation.ElementType.*;  
  8. importstatic java.lang.annotation.RetentionPolicy.*;  
  9. /** 
  10.  * <p>User: Zhang Kaitao 
  11.  * <p>Date: 13-12-15 
  12.  * <p>Version: 1.0 
  13.  */
  14. @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })  
  15. @Retention(RUNTIME)  
  16. //指定驗證器
  17. @Constraint(validatedBy = ForbiddenValidator.class)  
  18. @Documented
  19. public@interface Forbidden {  
  20.     //預設錯誤訊息
  21.     String message() default"{forbidden.word}";  
  22.     //分組
  23.     Class<?>[] groups() default { };  
  24.     //負載
  25.     Class<? extends Payload>[] payload() default { };  
  26.     //指定多個時使用
  27.     @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })  
  28.     @Retention(RUNTIME)  
  29.     @Documented
  30.     @interface List {  
  31.         Forbidden[] value();  
  32.     }  
  33. }  

2、 定義驗證器

Java程式碼  收藏程式碼
  1. package com.sishuok.spring4.validator;  
  2. import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorContextImpl;  
  3. import org.springframework.beans.factory.annotation.Autowired;  
  4. import org.springframework.context.ApplicationContext;  
  5. import org.springframework.util.StringUtils;  
  6. import javax.validation.ConstraintValidator;  
  7. import javax.validation.ConstraintValidatorContext;  
  8. import java.io.Serializable;  
  9. /** 
  10.  * <p>User: Zhang Kaitao 
  11.  * <p>Date: 13-12-15 
  12.  * <p>Version: 1.0 
  13.  */
  14. publicclass ForbiddenValidator implements ConstraintValidator<Forbidden, String> {  
  15.     private String[] forbiddenWords = {"admin"};  
  16.     @Override
  17.     publicvoid initialize(Forbidden constraintAnnotation) {  
  18.         //初始化,得到註解資料
  19.     }  
  20.     @Override
  21.     publicboolean isValid(String value, ConstraintValidatorContext context) {  
  22.         if(StringUtils.isEmpty(value)) {  
  23.             returntrue;  
  24.         }  
  25.         for(String word : forbiddenWords) {  
  26.             if(value.contains(word)) {  
  27.                 returnfalse;//驗證失敗
  28.             }  
  29.         }  
  30.         returntrue;  
  31.     }  
  32. }  

 驗證器中可以使用spring的依賴注入,如注入:@Autowired  private ApplicationContext ctx; 

3、使用

Java程式碼  收藏程式碼
  1. publicclass User implements Serializable {  
  2.     @Forbidden()  
  3.     private String name;  
  4. }  

4、當我們在提交name中含有admin的時候會輸出錯誤訊息:

Java程式碼  收藏程式碼
  1. forbidden.word=您輸入的資料中有非法關鍵詞  

問題來了,哪個詞是非法的呢?bean validation 和 hibernate validator都沒有提供相應的api提供這個資料,怎麼辦呢?通過跟蹤程式碼,發現一種不是特別好的方法:我們可以覆蓋org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl實現(即複製一份程式碼放到我們的src中),然後覆蓋buildAnnotationParameterMap方法;

Java程式碼  收藏程式碼
  1. private Map<String, Object> buildAnnotationParameterMap(Annotation annotation) {  
  2.     ……  
  3.     //將Collections.unmodifiableMap( parameters );替換為如下語句
  4.     return parameters;  
  5. }  

 即允許這個資料可以修改;然後在ForbiddenValidator中:

Java程式碼  收藏程式碼
  1. for(String word : forbiddenWords) {  
  2.     if(value.contains(word)) {  
  3.         ((ConstraintValidatorContextImpl)context).getConstraintDescriptor().getAttributes().put("word", word);  
  4.         returnfalse;//驗證失敗
  5.     }  
  6. }  

通過((ConstraintValidatorContextImpl)context).getConstraintDescriptor().getAttributes().put("word", word);新增自己的屬性;放到attributes中的資料可以通過${} 獲取。然後訊息就可以變成:

Java程式碼  收藏程式碼
  1. forbidden.word=您輸入的資料中有非法關鍵詞【{word}】  

這種方式不是很友好,但是可以解決我們的問題。

典型的如密碼、確認密碼的場景,非常常用;如果沒有這個功能我們需要自己寫程式碼來完成;而且經常重複自己。接下來看看bean validation 1.1如何實現的。

6、類級別驗證器

6.1、定義驗證註解

Java程式碼  收藏程式碼
  1. package com.sishuok.spring4.validator;  
  2. import javax.validation.Constraint;  
  3. import javax.validation.Payload;  
  4. import javax.validation.constraints.NotNull;  
  5. import java.lang.annotation.Documented;  
  6. import java.lang.annotation.Retention;  
  7. import java.lang.annotation.Target;  
  8. importstatic java.lang.annotation.ElementType.*;  
  9. importstatic java.lang.annotation.RetentionPolicy.*;  
  10. /** 
  11.  * <p>User: Zhang Kaitao 
  12.  * <p>Date: 13-12-15 
  13.  * <p>Version: 1.0 
  14.  */
  15. @Target({ TYPE, ANNOTATION_TYPE})  
  16. @Retention(RUNTIME)  
  17. //指定驗證器
  18. @Constraint(validatedBy = CheckPasswordValidator.class)  
  19. @Documented
  20. public@interface CheckPassword {  
  21.     //預設錯誤訊息
  22.     String message() default"";  
  23.     //分組
  24.     Class<?>[] groups() default { };  
  25.     //負載
  26.     Class<? extends Payload>[] payload() default { };  
  27.     //指定多個時使用
  28.     @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })  
  29.     @Retention(RUNTIME)  
  30.     @Documented
  31.     @interface List {  
  32.         CheckPassword[] value();  
  33.     }  
  34. }  

6.2、 定義驗證器

Java程式碼  收藏程式碼
  1. package com.sishuok.spring4.validator;  
  2. import com.sishuok.spring4.entity.User;  
  3. import org.springframework.util.StringUtils;  
  4. import javax.validation.ConstraintValidator;  
  5. import javax.validation.ConstraintValidatorContext;  
  6. /** 
  7.  * <p>User: Zhang Kaitao 
  8.  * <p>Date: 13-12-15 
  9.  * <p>Version: 1.0 
  10.  */
  11. publicclass CheckPasswordValidator implements ConstraintValidator<CheckPassword, User> {  
  12.     @Override
  13.     publicvoid initialize(CheckPassword constraintAnnotation) {  
  14.     }  
  15.     @Override
  16.     publicboolean isValid(User user, ConstraintValidatorContext context) {  
  17.         if(user == null) {  
  18.             returntrue;  
  19.         }  
  20.         //沒有填密碼
  21.         if(!StringUtils.hasText(user.getPassword())) {  
  22.             context.disableDefaultConstraintViolation();  
  23.             context.buildConstraintViolationWithTemplate("{password.null}")  
  24.                     .addPropertyNode("password")  
  25.                     .addConstraintViolation();  
  26.             returnfalse;  
  27.         }  
  28.         if(!StringUtils.hasText(user.getConfirmation())) {  
  29.             context.disableDefaultConstraintViolation();  
  30.             context.buildConstraintViolationWithTemplate("{password.confirmation.null}")  
  31.                     .addPropertyNode("confirmation")  
  32.                     .addConstraintViolation();  
  33.             returnfalse;  
  34.         }  
  35.         //兩次密碼不一樣
  36.         if (!user.getPassword().trim().equals(user.getConfirmation().trim())) {  
  37.             context.disableDefaultConstraintViolation();  
  38.             context.buildConstraintViolationWithTemplate("{password.confirmation.error}")  
  39.                     .addPropertyNode("confirmation")  
  40.                     .addConstraintViolation();  
  41.             returnfalse;  
  42.         }  
  43.         returntrue;  
  44.     }  
  45. }  

其中我們通過disableDefaultConstraintViolation禁用預設的約束;然後通過buildConstraintViolationWithTemplate(訊息模板)/addPropertyNode(所屬屬性)/addConstraintViolation定義我們自己的約束。

6.3、使用

Java程式碼  收藏程式碼
  1. @CheckPassword()  
  2. publicclass User implements Serializable {  
  3. }  

 放到類頭上即可。

7、通過指令碼驗證

Java程式碼  收藏程式碼
  1. @ScriptAssert(script = "_this.password==_this.confirmation", lang = "javascript", alias = "_this", message = "{password.confirmation.error}")  
  2. publicclass User implements Serializable {  
  3. }  

通過指令碼驗證是非常簡單而且強大的,lang指定指令碼語言(請參考javax.script.ScriptEngineManager JSR-223),alias是在指令碼驗證中User物件的名字,但是大家會發現一個問題:錯誤訊息怎麼顯示呢? 在springmvc 中會新增到全域性錯誤訊息中,這肯定不是我們想要的,我們改造下吧。

7.1、定義驗證註解

Java程式碼  收藏程式碼
  1. package com.sishuok.spring4.validator;  
  2. import org.hibernate.validator.internal.constraintvalidators.ScriptAssertValidator;  
  3. import java.lang.annotation.Documented;  
  4. import java.lang.annotation.Retention;  
  5. import java.lang.annotation.Target;  
  6. import javax.validation.Constraint;  
  7. import javax.validation.Payload;  
  8. importstatic java.lang.annotation.ElementType.TYPE;  
  9. importstatic java.lang.annotation.RetentionPolicy.RUNTIME;  
  10. @Target({ TYPE })  
  11. @Retention(RUNTIME)  
  12. @Constraint(validatedBy = {PropertyScriptAssertValidator.class})  
  13. @Documented
  14. public@interface PropertyScriptAssert {  
  15.     String message() default"{org.hibernate.validator.constraints.ScriptAssert.message}";  
  16.     Class<?>[] groups() default { };  
  17.     Class<? extends Payload>[] payload() default { };  
  18.     String lang();  
  19.     String script();  
  20.     String alias() default"_this";  
  21.     String property();  
  22.     @Target({ TYPE })  
  23.     @Retention(RUNTIME)  
  24.     @Documented
  25.     public@interface List {  
  26.         PropertyScriptAssert[] value();  
  27.     }  
  28. }  

和ScriptAssert沒什麼區別,只是多了個property用來指定出錯後給實體的哪個屬性。

7.2、驗證器

Java程式碼  收藏程式碼
  1. package com.sishuok.spring4.validator;  
  2. import javax.script.ScriptException;  
  3. import javax.validation.ConstraintDeclarationException;  
  4. import javax.validation.ConstraintValidator;  
  5. import javax.validation.ConstraintValidatorContext;  
  6. import com.sishuok.spring4.validator.PropertyScriptAssert;  
  7. import org.hibernate.validator.constraints.ScriptAssert;  
  8. import org.hibernate.validator.internal.util.Contracts;  
  9. import org.hibernate.validator.internal.util.logging.Log;  
  10. import org.hibernate.validator.internal.util.logging.LoggerFactory;  
  11. import org.hibernate.validator.internal.util.scriptengine.ScriptEvaluator;  
  12. import org.hibernate.validator.internal.util.scriptengine.ScriptEvaluatorFactory;  
  13. importstatic org.hibernate.validator.internal.util.logging.Messages.MESSAGES;  
  14. publicclass PropertyScriptAssertValidator implements ConstraintValidator<PropertyScriptAssert, Object> {  
  15.     privatestaticfinal Log log = LoggerFactory.make();  
  16.     private String script;  
  17.     private String languageName;  
  18.     private String alias;  
  19.     private String property;  
  20.     private String message;  
  21.     publicvoid initialize(PropertyScriptAssert constraintAnnotation) {  
  22.         validateParameters( constraintAnnotation );  
  23.         this.script = constraintAnnotation.script();  
  24.         this.languageName = constraintAnnotation.lang();  
  25.         this.alias = constraintAnnotation.alias();  
  26.         this.property = constraintAnnotation.property();  
  27.         this.message = constraintAnnotation.message();  
  28.     }  
  29.     publicboolean isValid(Object value, ConstraintValidatorContext constraintValidatorContext) {  
  30.         Object evaluationResult;  
  31.         ScriptEvaluator scriptEvaluator;  
  32.         try {  
  33.             ScriptEvaluatorFactory evaluatorFactory = ScriptEvaluatorFactory.getInstance();  
  34.             scriptEvaluator = evaluatorFactory.getScriptEvaluatorByLanguageName( languageName );  
  35.         }  
  36.         catch ( ScriptException e ) {  
  37.             thrownew ConstraintDeclarationException( e );  
  38.         }  
  39.         try {  
  40.             evaluationResult = scriptEvaluator.evaluate( script, value, alias );  
  41.         }  
  42.         catch ( ScriptException e ) {  
  43.             throw log.getErrorDuringScriptExecutionException( script, e );  
  44.         }  
  45.         if ( evaluationResult == null ) {  
  46.             throw log.getScriptMustReturnTrueOrFalseException( script );  
  47.         }  
  48.         if ( !( evaluationResult instanceof Boolean ) ) {  
  49.             throw log.getScriptMustReturnTrueOrFalseException(  
  50.                     script,  
  51.                     evaluationResult,  
  52.                     evaluationResult.getClass().getCanonicalName()  
  53.             );  
  54.         }  
  55.         if(Boolean.FALSE.equals(evaluationResult)) {  
  56.             constraintValidatorContext.disableDefaultConstraintViolation();  
  57.             constraintValidatorContext  
  58.                     .buildConstraintViolationWithTemplate(message)  
  59.                     .addPropertyNode(property)  
  60.                     .addConstraintViolation();  
  61.         }  
  62.         return Boolean.TRUE.equals( evaluationResult );  
  63.     }  
  64.     privatevoid validateParameters(PropertyScriptAssert constraintAnnotation) {  
  65.         Contracts.assertNotEmpty( constraintAnnotation.script(), MESSAGES.parameterMustNotBeEmpty( "script" ) );  
  66.         Contracts.assertNotEmpty( constraintAnnotation.lang(), MESSAGES.parameterMustNotBeEmpty( "lang" ) );  
  67.         Contracts.assertNotEmpty( constraintAnnotation.alias(), MESSAGES.parameterMustNotBeEmpty( "alias" ) );  
  68.         Contracts.assertNotEmpty( constraintAnnotation.property(), MESSAGES.parameterMustNotBeEmpty( "property" ) );  
  69.         Contracts.assertNotEmpty( constraintAnnotation.message(), MESSAGES.parameterMustNotBeEmpty( "message" ) );  
  70.     }  
  71. }  

和之前的類級別驗證器類似,就不多解釋了,其他程式碼全部拷貝自org.hibernate.validator.internal.constraintvalidators.ScriptAssertValidator。

7.3、使用

Java程式碼  收藏程式碼
  1. @PropertyScriptAssert(property = "confirmation", script = "_this.password==_this.confirmation", lang = "javascript", alias = "_this", message = "{password.confirmation.error}")  

和之前的區別就是多了個property,用來指定出錯時給哪個欄位。 這個相對之前的類級別驗證器更通用一點。

8、cross-parameter,跨引數驗證

直接看示例;

Java程式碼  收藏程式碼
  1. <bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">  
  2.     <property name="validator" ref="validator"/>  
  3. </bean>  

8.2、Service 

Java程式碼  收藏程式碼
  1. @Validated
  2. @Service
  3. publicclass UserService {  
  4.     @CrossParameter
  5.     publicvoid changePassword(String password, String confirmation) {  
  6.     }  
  7. }  

通過@Validated註解UserService表示該類中有需要進行方法引數/返回值驗證;   @CrossParameter註解方法表示要進行跨引數驗證;即驗證password和confirmation是否相等。

8.3、驗證註解 

Java程式碼  收藏程式碼
  1. package com.sishuok.spring4.validator;  
  2. //省略import
  3. @Constraint(validatedBy = CrossParameterValidator.class)  
  4. @Target({ METHOD, CONSTRUCTOR, ANNOTATION_TYPE })  
  5. @Retention(RUNTIME)  
  6. @Documented
  7. public@interface CrossParameter {  
  8.     String message() default"{password.confirmation.error}";  
  9.     Class<?>[] groups() default { };  
  10.     Class<? extends Payload>[] payload() default { };  
  11. }  

8.4、驗證器 

Java程式碼  收藏程式碼
  1. package com.sishuok.spring4.validator;  
  2. //省略import
  3. @SupportedValidationTarget(ValidationTarget.PARAMETERS)