一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Spring4新特性

 跬步小城 2014-05-07

Spring4新特性——泛型限定式依賴注入

Spring4新特性——核心容器的其他改進(jìn)

Spring4新特性——Web開發(fā)的增強(qiáng)

Spring4新特性——集成Bean Validation 1.1(JSR-349)到SpringMVC 

Spring4新特性——Groovy Bean定義DSL

Spring4新特性——更好的Java泛型操作API 

Spring4新特性——JSR310日期API的支持

Spring4新特性——注解、腳本、任務(wù)、MVC等其他特性改進(jìn) 

 

在之前的《跟我學(xué)SpringMVC》中的《第七章 注解式控制器的數(shù)據(jù)驗(yàn)證、類型轉(zhuǎn)換及格式化》中已經(jīng)介紹過SpringMVC集成Bean Validation 1.0(JSR-303),目前Bean Validation最新版本是Bean Validation 1.1(JSR-349),新特性可以到官網(wǎng)查看,筆者最喜歡的兩個(gè)特性是:跨參數(shù)驗(yàn)證(比如密碼和確認(rèn)密碼的驗(yàn)證)和支持在消息中使用EL表達(dá)式,其他的還有如方法參數(shù)/返回值驗(yàn)證、CDI和依賴注入、分組轉(zhuǎn)換等。對(duì)于方法參數(shù)/返回值驗(yàn)證,大家可以參閱《Spring3.1 對(duì)Bean Validation規(guī)范的新支持(方法級(jí)別驗(yàn)證) 》。

 

Bean Validation 1.1當(dāng)前實(shí)現(xiàn)是Hibernate validator 5,且spring4才支持。接下來(lái)我們從以下幾個(gè)方法講解Bean Validation 1.1,當(dāng)然不一定是新特性:

  1.  集成Bean Validation 1.1到SpringMVC
  2.  分組驗(yàn)證、分組順序及級(jí)聯(lián)驗(yàn)證
  3.  消息中使用EL表達(dá)式
  4.  方法參數(shù)/返回值驗(yàn)證
  5.  自定義驗(yàn)證規(guī)則
  6.  類級(jí)別驗(yàn)證器
  7.  腳本驗(yàn)證器
  8.  cross-parameter,跨參數(shù)驗(yàn)證
  9. 混合類級(jí)別驗(yàn)證器和跨參數(shù)驗(yàn)證器
  10. 組合多個(gè)驗(yàn)證注解
  11. 本地化

因?yàn)榇蠖鄶?shù)時(shí)候驗(yàn)證都配合web框架使用,而且很多朋友都咨詢過如分組/跨參數(shù)驗(yàn)證,所以本文介紹下這些,且是和SpringMVC框架集成的例子,其他使用方式(比如集成到JPA中)可以參考其官方文檔:

規(guī)范:http:///1.1/spec/

hibernate validator文檔:http:///validator/ 

 

 1、集成Bean Validation 1.1到SpringMVC

1.1、項(xiàng)目搭建

首先添加hibernate validator 5依賴:

Java代碼 復(fù)制代碼 收藏代碼
  1. <dependency>  
  2.     <groupId>org.hibernate</groupId>  
  3.     <artifactId>hibernate-validator</artifactId>  
  4.     <version>5.0.2.Final</version>  
  5. </dependency>  

如果想在消息中使用EL表達(dá)式,請(qǐng)確保EL表達(dá)式版本是 2.2或以上,如使用Tomcat6,請(qǐng)到Tomcat7中拷貝相應(yīng)的EL jar包到Tomcat6中。

Java代碼 復(fù)制代碼 收藏代碼
  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>  

請(qǐng)確保您使用的Web容器有相應(yīng)版本的el jar包。

 

對(duì)于其他POM依賴請(qǐng)下載附件中的項(xiàng)目參考。

 

1.2、Spring MVC配置文件(spring-mvc.xml):

Java代碼 復(fù)制代碼 收藏代碼
  1. <!-- 指定自己定義的validator -->  
  2. <mvc:annotation-driven validator="validator"/>  
  3.   
  4. <!-- 以下 validator  ConversionService 在使用 mvc:annotation-driven 會(huì) 自動(dòng)注冊(cè)-->  
  5. <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">  
  6.     <property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>  
  7.     <!-- 如果不加默認(rèn)到 使用classpath下的 ValidationMessages.properties -->  
  8.     <property name="validationMessageSource" ref="messageSource"/>  
  9. </bean>  
  10.   
  11. <!-- 國(guó)際化的消息資源文件(本系統(tǒng)中主要用于顯示/錯(cuò)誤消息定制) -->  
  12. <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">  
  13.     <property name="basenames">  
  14.         <list>  
  15.             <!-- 在web環(huán)境中一定要定位到classpath 否則默認(rèn)到當(dāng)前web應(yīng)用下找  -->  
  16.             <value>classpath:messages</value>  
  17.             <value>classpath:org/hibernate/validator/ValidationMessages</value>  
  18.         </list>  
  19.     </property>  
  20.     <property name="useCodeAsDefaultMessage" value="false"/>  
  21.     <property name="defaultEncoding" value="UTF-8"/>  
  22.     <property name="cacheSeconds" value="60"/>  
  23. </bean>  

此處主要把bean validation的消息查找委托給spring的messageSource。

 

1.3、實(shí)體驗(yàn)證注解:

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

對(duì)于驗(yàn)證規(guī)則可以參考官方文檔,或者《第七章 注解式控制器的數(shù)據(jù)驗(yàn)證、類型轉(zhuǎn)換及格式化》。

 

1.4、錯(cuò)誤消息文件messages.properties:

Java代碼 復(fù)制代碼 收藏代碼
  1. user.id.null=用戶編號(hào)不能為空  
  2. user.name.null=用戶名不能為空  
  3. user.name.length.illegal=用戶名長(zhǎng)度必須在520之間  
  4. user.name.illegal=用戶名必須是字母  
  5. user.password.null=密碼不能為空  

 

1.5、控制器

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

 

1.6、錯(cuò)誤頁(yè)面:

Java代碼 復(fù)制代碼 收藏代碼
  1. <spring:hasBindErrors name="user">  
  2.     <c:if test="${errors.fieldErrorCount > 0}">  
  3.         字段錯(cuò)誤:<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.   
  10.     <c:if test="${errors.globalErrorCount > 0}">  
  11.         全局錯(cuò)誤:<br/>  
  12.         <c:forEach items="${errors.globalErrors}" var="error">  
  13.             <spring:message var="message" code="${error.code}" arguments="${error.arguments}" text="${error.defaultMessage}"/>  
  14.             <c:if test="${not empty message}">  
  15.                 ${message}<br/>  
  16.             </c:if>  
  17.         </c:forEach>  
  18.     </c:if>  
  19. </spring:hasBindErrors>  

 

大家以后可以根據(jù)這個(gè)做通用的錯(cuò)誤消息顯示規(guī)則。比如我前端頁(yè)面使用validationEngine顯示錯(cuò)誤消息,那么我可以定義一個(gè)tag來(lái)通用化錯(cuò)誤消息的顯示:showFieldError.tag。  

 

1.7、測(cè)試

輸入如:http://localhost:9080/spring4/save?name=123 , 我們得到如下錯(cuò)誤:

Java代碼 復(fù)制代碼 收藏代碼
  1. name------用戶名必須是字母  
  2. name------用戶名長(zhǎng)度必須在520之間  
  3. password------密碼不能為空  
  4. id------用戶編號(hào)不能為空  

 

基本的集成就完成了。

 

如上測(cè)試有幾個(gè)小問題:

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

2、我想顯示如:用戶名【zhangsan】必須在5到20之間;其中我們想動(dòng)態(tài)顯示:用戶名、min,max;而不是寫死了;

3、我想在修改的時(shí)候只驗(yàn)證用戶名,其他的不驗(yàn)證怎么辦。

接下來(lái)我們挨著試試吧。

 

2、分組驗(yàn)證及分組順序

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

首先定義分組接口:

Java代碼 復(fù)制代碼 收藏代碼
  1. public interface First {  
  2. }  
  3.   
  4. public interface Second {  
  5. }  

分組接口就是兩個(gè)普通的接口,用于標(biāo)識(shí),類似于java.io.Serializable。

 

接著我們使用分組接口標(biāo)識(shí)實(shí)體:

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

 

驗(yàn)證時(shí)使用如:

Java代碼 復(fù)制代碼 收藏代碼
  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注解標(biāo)識(shí)要驗(yàn)證的分組;如果要驗(yàn)證兩個(gè)的話,可以這樣@Validated({First.class, Second.class})。

 

接下來(lái)我們來(lái)看看通過分組來(lái)指定順序;還記得之前的錯(cuò)誤消息嗎? user.name會(huì)顯示兩個(gè)錯(cuò)誤消息,而且順序不確定;如果我們先驗(yàn)證一個(gè)消息;如果不通過再驗(yàn)證另一個(gè)怎么辦?可以通過@GroupSequence指定分組驗(yàn)證順序:

 

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

通過@GroupSequence指定驗(yàn)證順序:先驗(yàn)證First分組,如果有錯(cuò)誤立即返回而不會(huì)驗(yàn)證Second分組,接著如果First分組驗(yàn)證通過了,那么才去驗(yàn)證Second分組,最后指定User.class表示那些沒有分組的在最后。這樣我們就可以實(shí)現(xiàn)按順序驗(yàn)證分組了。

 

另一個(gè)比較常見的就是級(jí)聯(lián)驗(yàn)證:

如:

Java代碼 復(fù)制代碼 收藏代碼
  1. public class User {  
  2.   
  3.     @Valid   
  4.     @ConvertGroup(from=First.class, to=Second.class)  
  5.     private Organization o;  
  6.   
  7. }  

 1、級(jí)聯(lián)驗(yàn)證只要在相應(yīng)的字段上加@Valid即可,會(huì)進(jìn)行級(jí)聯(lián)驗(yàn)證;@ConvertGroup的作用是當(dāng)驗(yàn)證o的分組是First時(shí),那么驗(yàn)證o的分組是Second,即分組驗(yàn)證的轉(zhuǎn)換。

 

3、消息中使用EL表達(dá)式

假設(shè)我們需要顯示如:用戶名[NAME]長(zhǎng)度必須在[MIN]到[MAX]之間,此處大家可以看到,我們不想把一些數(shù)據(jù)寫死,如NAME、MIN、MAX;此時(shí)我們可以使用EL表達(dá)式。

 

如:

Java代碼 復(fù)制代碼 收藏代碼
  1. @Length(min = 5, max = 20, message = "{user.name.length.illegal}", groups = {First.class})  

錯(cuò)誤消息:

Java代碼 復(fù)制代碼 收藏代碼
  1. user.name.length.illegal=用戶名長(zhǎng)度必須在{min}到{max}之間  

 

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

 

到此,我們還是無(wú)法得到出錯(cuò)的那個(gè)輸入值,如name=zhangsan。此時(shí)就需要EL表達(dá)式的支持,首先確定引入EL jar包且版本正確。然后使用如:

Java代碼 復(fù)制代碼 收藏代碼
  1. user.name.length.illegal=用戶名[${validatedValue}]長(zhǎng)度必須在520之間  

使用如EL表達(dá)式:${validatedValue}得到輸入的值,如zhangsan。當(dāng)然我們還可以使用如${min > 1 ? '大于1' : '小于等于1'},及在EL表達(dá)式中也能拿到如@Length的min等數(shù)據(jù)。

 

另外我們還可以拿到一個(gè)java.util.Formatter類型的formatter變量進(jìn)行格式化:

Java代碼 復(fù)制代碼 收藏代碼
  1. ${formatter.format("%04d", min)}  

 

4、方法參數(shù)/返回值驗(yàn)證

這個(gè)可以參考《Spring3.1 對(duì)Bean Validation規(guī)范的新支持(方法級(jí)別驗(yàn)證) 》,概念是類似的,具體可以參考Bean Validation 文檔。

 

5、自定義驗(yàn)證規(guī)則

有時(shí)候默認(rèn)的規(guī)則可能還不夠,有時(shí)候還需要自定義規(guī)則,比如屏蔽關(guān)鍵詞驗(yàn)證是非常常見的一個(gè)功能,比如在發(fā)帖時(shí)帖子中不允許出現(xiàn)admin等關(guān)鍵詞。

 

1、定義驗(yàn)證注解

Java代碼 復(fù)制代碼 收藏代碼
  1. package com.sishuok.spring4.validator;  
  2.   
  3. import javax.validation.Constraint;  
  4. import javax.validation.Payload;  
  5. import java.lang.annotation.Documented;  
  6. import java.lang.annotation.Retention;  
  7. import java.lang.annotation.Target;  
  8. import static java.lang.annotation.ElementType.*;  
  9. import static java.lang.annotation.RetentionPolicy.*;  
  10. /** 
  11.  * <p>User: Zhang Kaitao 
  12.  * <p>Date: 13-12-15 
  13.  * <p>Version: 1.0 
  14.  */  
  15.   
  16. @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })  
  17. @Retention(RUNTIME)  
  18. //指定驗(yàn)證器  
  19. @Constraint(validatedBy = ForbiddenValidator.class)  
  20. @Documented  
  21. public @interface Forbidden {  
  22.   
  23.     //默認(rèn)錯(cuò)誤消息  
  24.     String message() default "{forbidden.word}";  
  25.   
  26.     //分組  
  27.     Class<?>[] groups() default { };  
  28.   
  29.     //負(fù)載  
  30.     Class<? extends Payload>[] payload() default { };  
  31.   
  32.     //指定多個(gè)時(shí)使用  
  33.     @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })  
  34.     @Retention(RUNTIME)  
  35.     @Documented  
  36.     @interface List {  
  37.         Forbidden[] value();  
  38.     }  
  39. }  

 

2、 定義驗(yàn)證器

Java代碼 復(fù)制代碼 收藏代碼
  1. package com.sishuok.spring4.validator;  
  2.   
  3. import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorContextImpl;  
  4. import org.springframework.beans.factory.annotation.Autowired;  
  5. import org.springframework.context.ApplicationContext;  
  6. import org.springframework.util.StringUtils;  
  7.   
  8. import javax.validation.ConstraintValidator;  
  9. import javax.validation.ConstraintValidatorContext;  
  10. import java.io.Serializable;  
  11.   
  12. /** 
  13.  * <p>User: Zhang Kaitao 
  14.  * <p>Date: 13-12-15 
  15.  * <p>Version: 1.0 
  16.  */  
  17. public class ForbiddenValidator implements ConstraintValidator<Forbidden, String> {  
  18.   
  19.     private String[] forbiddenWords = {"admin"};  
  20.   
  21.     @Override  
  22.     public void initialize(Forbidden constraintAnnotation) {  
  23.         //初始化,得到注解數(shù)據(jù)  
  24.     }  
  25.   
  26.     @Override  
  27.     public boolean isValid(String value, ConstraintValidatorContext context) {  
  28.         if(StringUtils.isEmpty(value)) {  
  29.             return true;  
  30.         }  
  31.   
  32.         for(String word : forbiddenWords) {  
  33.             if(value.contains(word)) {  
  34.                 return false;//驗(yàn)證失敗  
  35.             }  
  36.         }  
  37.         return true;  
  38.     }  
  39. }  

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

 

3、使用

Java代碼 復(fù)制代碼 收藏代碼
  1. public class User implements Serializable {  
  2.     @Forbidden()  
  3.     private String name;  
  4. }  

 

4、當(dāng)我們?cè)谔峤籲ame中含有admin的時(shí)候會(huì)輸出錯(cuò)誤消息:

Java代碼 復(fù)制代碼 收藏代碼
  1. forbidden.word=您輸入的數(shù)據(jù)中有非法關(guān)鍵詞  

 

問題來(lái)了,哪個(gè)詞是非法的呢?bean validation 和 hibernate validator都沒有提供相應(yīng)的api提供這個(gè)數(shù)據(jù),怎么辦呢?通過跟蹤代碼,發(fā)現(xiàn)一種不是特別好的方法:我們可以覆蓋org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl實(shí)現(xiàn)(即復(fù)制一份代碼放到我們的src中),然后覆蓋buildAnnotationParameterMap方法;

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

 即允許這個(gè)數(shù)據(jù)可以修改;然后在ForbiddenValidator中:

Java代碼 復(fù)制代碼 收藏代碼
  1. for(String word : forbiddenWords) {  
  2.     if(value.contains(word)) {  
  3.         ((ConstraintValidatorContextImpl)context).getConstraintDescriptor().getAttributes().put("word", word);  
  4.         return false;//驗(yàn)證失敗  
  5.     }  
  6. }  

通過((ConstraintValidatorContextImpl)context).getConstraintDescriptor().getAttributes().put("word", word);添加自己的屬性;放到attributes中的數(shù)據(jù)可以通過${} 獲取。然后消息就可以變成:

Java代碼 復(fù)制代碼 收藏代碼
  1. forbidden.word=您輸入的數(shù)據(jù)中有非法關(guān)鍵詞【{word}】  

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

 

典型的如密碼、確認(rèn)密碼的場(chǎng)景,非常常用;如果沒有這個(gè)功能我們需要自己寫代碼來(lái)完成;而且經(jīng)常重復(fù)自己。接下來(lái)看看bean validation 1.1如何實(shí)現(xiàn)的。

 

6、類級(jí)別驗(yàn)證器

6.1、定義驗(yàn)證注解

Java代碼 復(fù)制代碼 收藏代碼
  1. package com.sishuok.spring4.validator;  
  2.   
  3. import javax.validation.Constraint;  
  4. import javax.validation.Payload;  
  5. import javax.validation.constraints.NotNull;  
  6. import java.lang.annotation.Documented;  
  7. import java.lang.annotation.Retention;  
  8. import java.lang.annotation.Target;  
  9. import static java.lang.annotation.ElementType.*;  
  10. import static java.lang.annotation.RetentionPolicy.*;  
  11. /** 
  12.  * <p>User: Zhang Kaitao 
  13.  * <p>Date: 13-12-15 
  14.  * <p>Version: 1.0 
  15.  */  
  16.   
  17. @Target({ TYPE, ANNOTATION_TYPE})  
  18. @Retention(RUNTIME)  
  19. //指定驗(yàn)證器  
  20. @Constraint(validatedBy = CheckPasswordValidator.class)  
  21. @Documented  
  22. public @interface CheckPassword {  
  23.   
  24.     //默認(rèn)錯(cuò)誤消息  
  25.     String message() default "";  
  26.   
  27.     //分組  
  28.     Class<?>[] groups() default { };  
  29.   
  30.     //負(fù)載  
  31.     Class<? extends Payload>[] payload() default { };  
  32.   
  33.     //指定多個(gè)時(shí)使用  
  34.     @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })  
  35.     @Retention(RUNTIME)  
  36.     @Documented  
  37.     @interface List {  
  38.         CheckPassword[] value();  
  39.     }  
  40. }  

6.2、 定義驗(yàn)證器

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

其中我們通過disableDefaultConstraintViolation禁用默認(rèn)的約束;然后通過buildConstraintViolationWithTemplate(消息模板)/addPropertyNode(所屬屬性)/addConstraintViolation定義我們自己的約束。

 

6.3、使用

Java代碼 復(fù)制代碼 收藏代碼
  1. @CheckPassword()  
  2. public class User implements Serializable {  
  3. }  

 放到類頭上即可。

 

7、通過腳本驗(yàn)證

Java代碼 復(fù)制代碼 收藏代碼
  1. @ScriptAssert(script = "_this.password==_this.confirmation", lang = "javascript", alias = "_this", message = "{password.confirmation.error}")  
  2. public class User implements Serializable {  
  3. }  

通過腳本驗(yàn)證是非常簡(jiǎn)單而且強(qiáng)大的,lang指定腳本語(yǔ)言(請(qǐng)參考javax.script.ScriptEngineManager JSR-223),alias是在腳本驗(yàn)證中User對(duì)象的名字,但是大家會(huì)發(fā)現(xiàn)一個(gè)問題:錯(cuò)誤消息怎么顯示呢? 在springmvc 中會(huì)添加到全局錯(cuò)誤消息中,這肯定不是我們想要的,我們改造下吧。

 

7.1、定義驗(yàn)證注解

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

和ScriptAssert沒什么區(qū)別,只是多了個(gè)property用來(lái)指定出錯(cuò)后給實(shí)體的哪個(gè)屬性。

 

7.2、驗(yàn)證器

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

和之前的類級(jí)別驗(yàn)證器類似,就不多解釋了,其他代碼全部拷貝自org.hibernate.validator.internal.constraintvalidators.ScriptAssertValidator。

 

7.3、使用

Java代碼 復(fù)制代碼 收藏代碼
  1. @PropertyScriptAssert(property = "confirmation", script = "_this.password==_this.confirmation", lang = "javascript", alias = "_this", message = "{password.confirmation.error}")  

和之前的區(qū)別就是多了個(gè)property,用來(lái)指定出錯(cuò)時(shí)給哪個(gè)字段。 這個(gè)相對(duì)之前的類級(jí)別驗(yàn)證器更通用一點(diǎn)。

 

8、cross-parameter,跨參數(shù)驗(yàn)證

直接看示例;

 

8.1、首先注冊(cè)MethodValidationPostProcessor,起作用請(qǐng)參考《Spring3.1 對(duì)Bean Validation規(guī)范的新支持(方法級(jí)別驗(yàn)證) 》 

Java代碼 復(fù)制代碼 收藏代碼
  1. <bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">  
  2.     <property name="validator" ref="validator"/>  
  3. </bean>  

 

8.2、Service 

Java代碼 復(fù)制代碼 收藏代碼
  1. @Validated  
  2. @Service  
  3. public class UserService {  
  4.   
  5.     @CrossParameter  
  6.     public void changePassword(String password, String confirmation) {  
  7.   
  8.     }  
  9. }  

通過@Validated注解UserService表示該類中有需要進(jìn)行方法參數(shù)/返回值驗(yàn)證;   @CrossParameter注解方法表示要進(jìn)行跨參數(shù)驗(yàn)證;即驗(yàn)證password和confirmation是否相等。

 

8.3、驗(yàn)證注解 

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

 

8.4、驗(yàn)證器 

Java代碼 復(fù)制代碼 收藏代碼
  1. package com.sishuok.spring4.validator;  
  2.   
  3. //省略import  
  4.   
  5. @SupportedValidationTarget(ValidationTarget.PARAMETERS)  
  6. public class CrossParameterValidator implements ConstraintValidator<CrossParameter, Object[]> {  
  7.   
  8.     @Override  
  9.     public void initialize(CrossParameter constraintAnnotation) {  
  10.     }  
  11.   
  12.     @Override  
  13.     public boolean isValid(Object[] value, ConstraintValidatorContext context) {  
  14.         if(value == null || value.length != 2) {  
  15.             throw new IllegalArgumentException("must have two args");  
  16.         }  
  17.         if(value[0] == null || value[1] == null) {  
  18.             return true;  
  19.         }  
  20.         if(value[0].equals(value[1])) {  
  21.             return true;  
  22.         }  
  23.         return false;  
  24.     }  
  25. }  

其中@SupportedValidationTarget(ValidationTarget.PARAMETERS)表示驗(yàn)證參數(shù); value將是參數(shù)列表。 

 

8.5、使用

Java代碼 復(fù)制代碼 收藏代碼
  1. @RequestMapping("/changePassword")  
  2. public String changePassword(  
  3.         @RequestParam("password") String password,  
  4.         @RequestParam("confirmation") String confirmation, Model model) {  
  5.     try {  
  6.         userService.changePassword(password, confirmation);  
  7.     } catch (ConstraintViolationException e) {  
  8.         for(ConstraintViolation violation : e.getConstraintViolations()) {  
  9.             System.out.println(violation.getMessage());  
  10.         }  
  11.     }  
  12.     return "success";  
  13. }  

調(diào)用userService.changePassword方法,如果驗(yàn)證失敗將拋出ConstraintViolationException異常,然后得到ConstraintViolation,調(diào)用getMessage即可得到錯(cuò)誤消息;然后到前臺(tái)顯示即可。

 

從以上來(lái)看,不如之前的使用方便,需要自己對(duì)錯(cuò)誤消息進(jìn)行處理。 下一節(jié)我們也寫個(gè)腳本方式的跨參數(shù)驗(yàn)證器。

 

9、混合類級(jí)別驗(yàn)證器和跨參數(shù)驗(yàn)證器

9.1、驗(yàn)證注解

Java代碼 復(fù)制代碼 收藏代碼
  1. package com.sishuok.spring4.validator;  
  2.   
  3. //省略import  
  4.   
  5. @Constraint(validatedBy = {  
  6.         CrossParameterScriptAssertClassValidator.class,  
  7.         CrossParameterScriptAssertParameterValidator.class  
  8. })  
  9. @Target({ TYPE, FIELD, PARAMETER, METHOD, CONSTRUCTOR, ANNOTATION_TYPE })  
  10. @Retention(RUNTIME)  
  11. @Documented  
  12. public @interface CrossParameterScriptAssert {  
  13.     String message() default "error";  
  14.     Class<?>[] groups() default { };  
  15.     Class<? extends Payload>[] payload() default { };  
  16.     String script();  
  17.     String lang();  
  18.     String alias() default "_this";  
  19.     String property() default "";  
  20.     ConstraintTarget validationAppliesTo() default ConstraintTarget.IMPLICIT;  
  21. }   

 

此處我們通過@Constraint指定了兩個(gè)驗(yàn)證器,一個(gè)類級(jí)別的,一個(gè)跨參數(shù)的。validationAppliesTo指定為ConstraintTarget.IMPLICIT,表示隱式自動(dòng)判斷。

 

9.2、驗(yàn)證器

請(qǐng)下載源碼查看

 

9.3、使用

9.3.1、類級(jí)別使用

Java代碼 復(fù)制代碼 收藏代碼
  1. @CrossParameterScriptAssert(property = "confirmation", script = "_this.password==_this.confirmation", lang = "javascript", alias = "_this", message = "{password.confirmation.error}")  

指定property即可,其他和之前的一樣。

9.3.2、跨參數(shù)驗(yàn)證

Java代碼 復(fù)制代碼 收藏代碼
  1. @CrossParameterScriptAssert(script = "args[0] == args[1]", lang = "javascript", alias = "args", message = "{password.confirmation.error}")  
  2. public void changePassword(String password, String confirmation) {  
  3.   
  4. }  

通過args[0]==args[1] 來(lái)判斷是否相等。

 

這樣,我們的驗(yàn)證注解就自動(dòng)適應(yīng)兩種驗(yàn)證規(guī)則了。  

 

10、組合驗(yàn)證注解 

有時(shí)候,可能有好幾個(gè)注解需要一起使用,此時(shí)就可以使用組合驗(yàn)證注解

Java代碼 復(fù)制代碼 收藏代碼
  1. @Target({ FIELD})  
  2. @Retention(RUNTIME)  
  3. @Documented  
  4. @NotNull(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.length.illegal}")  
  7. @Constraint(validatedBy = { })  
  8. public @interface Composition {  
  9.     String message() default "";  
  10.     Class<?>[] groups() default { };  
  11.     Class<? extends Payload>[] payload() default { };  
  12. }  

這樣我們驗(yàn)證時(shí)只需要:

Java代碼 復(fù)制代碼 收藏代碼
  1. @Composition()  
  2. private String name;  

簡(jiǎn)潔多了。 

 

11、本地化 

即根據(jù)不同的語(yǔ)言選擇不同的錯(cuò)誤消息顯示。

1、本地化解析器

Java代碼 復(fù)制代碼 收藏代碼
  1. <bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver">  
  2.     <property name="cookieName" value="locale"/>  
  3.     <property name="cookieMaxAge" value="-1"/>  
  4.     <property name="defaultLocale" value="zh_CN"/>  
  5. </bean>  

此處使用cookie存儲(chǔ)本地化信息,當(dāng)然也可以選擇其他的,如Session存儲(chǔ)。

 

2、設(shè)置本地化信息的攔截器

Java代碼 復(fù)制代碼 收藏代碼
  1. <mvc:interceptors>  
  2.     <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">  
  3.         <property name="paramName" value="language"/>  
  4.     </bean>  
  5. </mvc:interceptors>  

即請(qǐng)求參數(shù)中通過language設(shè)置語(yǔ)言。

 

3、消息文件

 

4、 瀏覽器輸入

http://localhost:9080/spring4/changePassword?password=1&confirmation=2&language=en_US

 

 

 

到此,我們已經(jīng)完成大部分Bean Validation的功能實(shí)驗(yàn)了。對(duì)于如XML配置、編程式驗(yàn)證API的使用等對(duì)于我們使用SpringMVC這種web環(huán)境用處不大,所以就不多介紹了,有興趣可以自己下載官方文檔學(xué)習(xí)。

 

 

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    亚洲综合激情另类专区老铁性| 国产超薄黑色肉色丝袜| 国产成人精品资源在线观看| 国产免费一区二区三区av大片| 亚洲高清中文字幕一区二区三区| 高清不卡一卡二卡区在线| 国产爆操白丝美女在线观看| 男生和女生哪个更好色| 激情综合五月开心久久| 亚洲综合激情另类专区老铁性| 久久精品亚洲精品一区| 很黄很污在线免费观看| 精品国产成人av一区二区三区| 欧美韩国日本精品在线| 国产亚洲中文日韩欧美综合网| 国产真人无遮挡免费视频一区| 亚洲精品一区三区三区| 在线观看欧美视频一区| 激情综合五月开心久久| 成人国产一区二区三区精品麻豆| 欧美丰满大屁股一区二区三区| 97人妻人人揉人人躁人人| 夜夜嗨激情五月天精品| 国产欧美一区二区三区精品视| 久久99午夜福利视频| 日韩精品免费一区二区三区| 少妇福利视频一区二区| 色一情一伦一区二区三| 国产原创中文av在线播放| 日本午夜免费啪视频在线| 日韩不卡一区二区在线| 国产精品免费福利在线| 亚洲中文字幕视频一区二区| 大屁股肥臀熟女一区二区视频| 欧美精品二区中文乱码字幕高清 | 国产一区二区三中文字幕| 欧美一级日韩中文字幕| 91精品蜜臀一区二区三区| 国产成人午夜在线视频| 中文字幕久热精品视频在线 | 男人大臿蕉香蕉大视频|