Java validator整理


Java validator整理

因為想對方法的入參和出參作簡單的非空或者非空字符做校驗,所以找了下相關的@NotNull注解

說明
javax.validation.constraints.NotNull Java提供的JSR校驗規范
org.jetbrains.annotations.NotNull idea提供的校驗注解,只在使用idea工具時有效,主要起注釋功能,提醒調用者
com.sun.istack.internal 還不清楚

這里主要看下Java的JSR規范提供的校驗
主要的實現框架有Hibernate validation和Spring Validation
依賴jar

<dependency>
   <groupId>javax.el</groupId>
   <artifactId>javax.el-api</artifactId>
   <version>2.2.4</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>5.1.3.Final</version>
</dependency>

JSR-303原生支持的限制有如下幾種:

限制 說明
@Null 限制只能為null
@NotNull 限制必須不為null
@AssertFalse 限制必須為false
@AssertTrue 限制必須為true
@DecimalMax(value) 限制必須為一個不大於指定值的數字
@DecimalMin(value) 限制必須為一個不小於指定值的數字
@Digits(integer,fraction) 限制必須為一個小數,且整數部分的位數不能超過integer,小數部分的位數不能超過fraction
@Future 限制必須是一個將來的日期
@Max(value) 限制必須為一個不大於指定值的數字
@Min(value) 限制必須為一個不小於指定值的數字
@Past 限制必須是一個過去的日期
@Pattern(value) 定的正則表達式
@Size(max,min) 限制字符長度必須在min到max之間

除此之外,hibernate也還提供了其它的限制校驗,在org.hibernate.validator.constraints包下
@NotBlank(message =) 驗證字符串非null,且長度必須大於0
@Email 被注釋的元素必須是電子郵箱地址
@Length(min=,max=) 被注釋的字符串的大小必須在指定的范圍內
@NotEmpty 被注釋的字符串的必須非空
@Range(min=,max=,message=) 被注釋的元素必須在合適的范圍內
單獨使用hibernate validation校驗bean對象
參考ytasd項目代碼

public class ValidatorUtil {
    private static Validator validator = Validation.buildDefaultValidatorFactory()
            .getValidator();
    public static <T> ValidatorResult validate(T obj){
        ValidatorResult result = new ValidatorResult();
        Set<ConstraintViolation<T>> set = validator.validate(obj,Default.class);
        if( CollectionUtils.isNotEmpty(set) ){
            result.setHasErrors(true);
            Map<String,String> errorMsg = new HashMap<String,String>();
            for(ConstraintViolation<T> cv : set){
                errorMsg.put(cv.getPropertyPath().toString(), cv.getMessage());
            }
            result.setErrorMsg(errorMsg);
        }
        return result;
    }

    public static <T> ValidatorResult validateProperty(T obj,String propertyName){
        ValidatorResult result = new ValidatorResult();
        Set<ConstraintViolation<T>> set = validator.validateProperty(obj,propertyName,Default.class);
        if( CollectionUtils.isNotEmpty(set) ){
            result.setHasErrors(true);
            Map<String,String> errorMsg = new HashMap<String,String>();
            for(ConstraintViolation<T> cv : set){
                errorMsg.put(propertyName, cv.getMessage());
            }
            result.setErrorMsg(errorMsg);
        }
        return result;
    }
}

整合Spring
Bean校驗處理器:BeanValidationPostProcessor
方法校驗處理器:MethodValidationPostProcessor
這里我們主要關心對方法的入參、出參校驗,所以主要看下MethodValidationPostProcessor的實現
關鍵代碼

private Class<? extends Annotation> validatedAnnotationType = Validated.class;
@Override
	public void afterPropertiesSet() {
		Pointcut pointcut = new AnnotationMatchingPointcut(this.validatedAnnotationType, true);
		Advice advice = (this.validator != null ? new MethodValidationInterceptor(this.validator) :
				new MethodValidationInterceptor());
		this.advisor = new DefaultPointcutAdvisor(pointcut, advice);
	}

通過這段代碼,我們可以看出,它主要對使用了Validated注解的類作攔截,攔截器是MethodValidationInterceptor
核心代碼

private static Method forExecutablesMethod;

	private static Method validateParametersMethod;

	private static Method validateReturnValueMethod;

	static {
		try {
		//這里拿到ExecutableValidator
			forExecutablesMethod = Validator.class.getMethod("forExecutables");
			Class<?> executableValidatorClass = forExecutablesMethod.getReturnType();
			validateParametersMethod = executableValidatorClass.getMethod(
					"validateParameters", Object.class, Method.class, Object[].class, Class[].class);
			validateReturnValueMethod = executableValidatorClass.getMethod(
					"validateReturnValue", Object.class, Method.class, Object.class, Class[].class);
		}
		catch (Exception ex) {
			// Bean Validation 1.1 ExecutableValidator API not available
		}
	}
public Object invoke(MethodInvocation invocation) throws Throwable {
		Class<?>[] groups = determineValidationGroups(invocation);
		if (forExecutablesMethod != null) {
			Object executableValidator = ReflectionUtils.invokeMethod(forExecutablesMethod, this.validator);
			Set<ConstraintViolation<?>> result = (Set<ConstraintViolation<?>>)
					ReflectionUtils.invokeMethod(validateParametersMethod, executableValidator,
							invocation.getThis(), invocation.getMethod(), invocation.getArguments(), groups);
			if (!result.isEmpty()) {
				throw new ConstraintViolationException(result);
			}
			Object returnValue = invocation.proceed();
			result = (Set<ConstraintViolation<?>>)
					ReflectionUtils.invokeMethod(validateReturnValueMethod, executableValidator,
							invocation.getThis(), invocation.getMethod(), returnValue, groups);
			if (!result.isEmpty()) {
				throw new ConstraintViolationException(result);
			}
			return returnValue;
		}
		else {
			return HibernateValidatorDelegate.invokeWithinValidation(invocation, this.validator, groups);
		}
	}

ExecutableValidator是關鍵的接口,它提供了對方法入參和返回值的校驗

validateParameters(T object, Method method, Object[] parameterValues, Class ... groups);

validateReturnValue(T object, Method method, Object returnValue, Class ... groups);

validateConstructorParameters(Constructor constructor, Object[] parameterValues, Class ... groups);

validateConstructorReturnValue(Constructor constructor, T createdObject, Class ... groups);

返回的都是Set >,ConstraintViolation中封裝了具體的信息

通過MethodValidationInterceptor可以看出,它主要幫我們做了自動調用校驗方法的邏輯,但使用上比較麻煩,需要在類上加上Validated

默認只對@org.springframework.validation.annotation.Validated注解的Bean進行驗證,我們可以修改validatedAnnotationType為其他注解類型來支持其他注解驗證。而且目前只支持Hibernate Validator實現,在未來版本可能支持其他實現。
Controller層的校驗只能使用BeanValidator?

我們可以自己寫個aop調用MethodValidationInterceptor

<bean class="org.springframework.validation.beanvalidation.MethodValidationInterceptor" id="validationInterceptor" />
    <aop:config>
        <aop:pointcut id="validator" expression="execution(* com.yt.trade..*(..))" />
        <aop:advisor advice-ref="validationInterceptor" pointcut-ref="validator"/>
    </aop:config>

限制:

  1. 只能對Spring代理的類和方法進行驗證,不能在內部調用中起效。
  2. 返回的信息類中沒有參數名,提示不直觀
    基於這兩個問題,我們可以自己寫攔截器作校驗

自定義校驗器

實現ConstraintValidator接口

@Target({ElementType.FIELD, ElementType.METHOD})  
@Retention(RetentionPolicy.RUNTIME)  
@Constraint(validatedBy=MinValidator.class)  
public @interface Min {  
   
    int value() default 0;  
     
    String message();  
     
    Class<?>[] groups() default {};  
     
    Class<? extends Payload>[] payload() default {};  
}  
public class MinValidator implements ConstraintValidator<Min, Integer> {  
   
    private int minValue;  
     
    public void initialize(Min min) {  
       // TODO Auto-generated method stub  
       //把Min限制類型的屬性value賦值給當前ConstraintValidator的成員變量minValue  
       minValue = min.value();  
    }  
   
    public boolean isValid(Integer value, ConstraintValidatorContext arg1) {  
       // TODO Auto-generated method stub  
       //在這里我們就可以通過當前ConstraintValidator的成員變量minValue訪問到當前限制類型Min的value屬性了  
       return value >= minValue;  
    }  
   
}  

參考:

Spring3.1 對Bean Validation規范的新支持(方法級別驗證)
http://www.iteye.com/topic/1122937
hibernate Validation使用示例及講解
在系統中使用Bean Validation驗證參數
http://haohaoxuexi.iteye.com/blog/1812584
http://my.oschina.net/qjx1208/blog/200946


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM