Spring數據校驗(LocalValidatorFactoryBean和MethodValidationPostProcessor的區別/@Valid和@Validated的區別)

JSR標準和Spring校驗框架

JavaJSR-303標準的數據校驗的核心接口是javax.validation.Validator,該接口根據目標對象中標註的校驗註解進行數據校驗,並得到校驗結果。

Spring也有自己的校驗框架,同時支持JSR-303標準的校驗框架。SpringDataBinder在進行數據綁定時,同時調用校驗框架完成數據校驗工作。
Spring的校驗框架包是org.springframework.validation,其中LocalValidatorFactoryBean同時實現了SpringValidatorJSR-303Validator接口,但是Spring本身沒有提供JSR-303的實現,因此必須將實現了JSR-303jar放在類路徑下。

配置校驗框架的處理器

<mvc:annotation-driven	validator="validator" />
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor" />

上面配置了LocalValidatorFactoryBeanMethodValidationPostProcessor兩個Bean,它們的區別:

  1. LocalValidatorFactoryBean同時實現了SpringValidatorJSR-303Validator接口
  2. LocalValidatorFactoryBean可以完成i18n
  3. MethodValidationPostProcessor可以實現在方法上校驗基本數據包裝類型和String類型等單獨的對象,但是要在異常處理器中處理ConstraintViolationException異常
  4. 兩個Bean可以同時存在

重要的兩個註解@Valid和@Validated

這兩個註解都是進行數據校驗的標誌,但是有區別:

  1. @Valid可以在方法/成員變量/構造函數/方法參數上使用;@Validated可以在類/方法/方法參數上使用;區別在於成員變量上是否可以使用
  2. 由於@Valid可以在成員變量上使用,因此可以嵌套校驗
  3. @Valid會把校驗不通過的信息交給BindingResult中,因此在controller的方法中,@ValidBindingResult要同時存在
  4. @Validated可以在類上使用,可以配合MethodValidationPostProcessor實現校驗基本數據包裝類型和String類型等單獨的對象

案例

  • 依賴

    <dependency>
    	<groupId>org.hibernate</groupId>
    	<artifactId>hibernate-validator</artifactId>
    	<version>5.2.4.Final</version>
    	<exclusions>
    		<exclusion>
    			<artifactId>validation-api</artifactId>
    			<groupId>javax.validation</groupId>
    		</exclusion>
    	</exclusions>
    </dependency>
    <dependency>
    	<groupId>javax.validation</groupId>
    	<artifactId>validation-api</artifactId>
    	<version>1.1.0.Final</version>
    </dependency>
    
  • @Validated

    @RestController
    @RequestMapping("/validated")
    @Validated
    public class ValidatedController {
        @RequestMapping("/test")
        public Object test(@NotNull(message = "不能爲null") String name) {
            return new Object();
        }
    }
    
    @RestControllerAdvice
    public class ExceptionHandle {
        @ExceptionHandler(ConstraintViolationException.class)
        @ResponseBody
        public Object constraintViolationException(ConstraintViolationException e) {
            StringBuilder stringBuilder = new StringBuilder();
            for (ConstraintViolation constraintViolation : e.getConstraintViolations()) {
                stringBuilder.append(constraintViolation.getMessage()).append(",");
            }
            if (stringBuilder.length() > 0) {
                stringBuilder.deleteCharAt(stringBuilder.length() - 1);
            }
            return stringBuilder.toString();
        }
    }
    
  • @Valid

    public class Person implements Serializable {
        @NotNull(message = "person.name_is_null")
        private String name;
        @Valid
        private Phone phone;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Phone getPhone() {
            return phone;
        }
        public void setPhone(Phone phone) {
            this.phone = phone;
        }
    }
    public class Phone implements Serializable {
        @NotNull(message = "phone.name_is_null")
        private String name;
        private String price;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getPrice() {
            return price;
        }
        public void setPrice(String price) {
            this.price = price;
        }
    }
    
    @ResponseBody
    @RequestMapping("/test")
    public Object test(@Valid Person person, BindingResult result) {
    	if(result != null){
    		if(result.hasErrors()){
    			for (FieldError fieldError : result.getFieldErrors()) {
    				System.out.println(fieldError.getDefaultMessage());
    			}
    		}
    	}
    	return new Object();
    }
    
  • 出錯的使用

    @ResponseBody
    @RequestMapping("/test")
    public Object testBindingResult(@NotBlank String name, BindingResult result) {
    	if (result.hasErrors()) {
    		System.out.println(result.getFieldError("name").getDefaultMessage());
    	}
    	return new Object();
    }
    

    會報錯:

    public Object test(@NotBlank String name, BindingResult result)
    嚴重: Servlet.service() for servlet [springmvc] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalStateException: An Errors/BindingResult argument is expected to be declared immediately after the model attribute, the @RequestBody or the @RequestPart arguments to which they apply: public com.p7.framework.common.ResultBean com.p7.framework.controller.BindingResultController.testBindingResult(java.lang.String,org.springframework.validation.BindingResult)] 
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章