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通過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>
限制:
- 只能對Spring代理的類和方法進行驗證,不能在內部調用中起效。
- 返回的信息類中沒有參數名,提示不直觀
基於這兩個問題,我們可以自己寫攔截器作校驗
自定義校驗器
實現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