前言

数据验证可以放在表现层中验证,但是如果有多个地方都需要验证同一个数据项呢,就会出现很多冗余,代码复用不高,而且万一需要改变数据验证的方式,那么就需要牵一发而动全身,导致工作难度加大。Hibernate有一个项目validator,实现了Bean Validation 2.0

引入依赖


<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-validator</artifactId>
	<version>4.2.0.Final</version>
</dependency>


依赖关系

引入了Hibernate数据验证的依赖包

|- 扩展Javax 的基本数据验证

|- Hibernate 数据类型校验


我们来随便分析一下他的一个验证约束、


@Documented
@Constraint(validatedBy = EmailValidator.class)
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
public @interface Email {
	String message() default "{org.hibernate.validator.constraints.Email.message}";

	Class<?>[] groups() default { };

	Class<? extends Payload>[] payload() default { };

	/**
	 * Defines several {@code @Email} annotations on the same element.
	 */
	@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
	@Retention(RUNTIME)
	@Documented
	public @interface List {
		Email[] value();
	}
}


 

注解只是一个标识,要进行数据比较还需要一个特定的约束类要判定,也就是注解中的Constraint(validatedBy=xxx)

这边来看一下他的约束


public class EmailValidator implements ConstraintValidator<Email, String> {
	private static String ATOM = "[a-z0-9!#$%&'*+/=?^_`{|}~-]";
	private static String DOMAIN = "(" + ATOM + "+(\\." + ATOM + "+)*";
	private static String IP_DOMAIN = "\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\]";

	private java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(
			"^" + ATOM + "+(\\." + ATOM + "+)*@"
					+ DOMAIN
					+ "|"
					+ IP_DOMAIN
					+ ")$",
			java.util.regex.Pattern.CASE_INSENSITIVE
	);

	public void initialize(Email annotation) {
	}

	public boolean isValid(String value, ConstraintValidatorContext context) {
		if ( value == null || value.length() == 0 ) {
			return true;
		}
		Matcher m = pattern.matcher( value );
		return m.matches();
	}
}

这里引申出一个命名规范

例如:

  • 验证数据必须是一个合法的Email,那么对应的注解名称就是Email
  • 验证器命名规范就是注解名称 + validator

 

验证器必须要实现特定的接口ConstraintValidator<Email, String>

Email:注解类型

String:验证的数据类型

 

实现两个方法:

Initialize:初始化操作,观察方法入参是一个Email,初步推测是检查注解的属性

isValid:验证操作,返回true验证成功,返回false验证失败。

 

HiBernate注解标配抽象方法


String message() default "{org.hibernate.validator.constraints.Email.message}";
	Class<?>[] groups() default { };
	Class<? extends Payload>[] payload() default { };


message 是当验证失败后提示的消息,可以直接硬编码写死也可以通过填写国际化key

简单的验证示例

 


public class Cat {

	@NotNull(message="id不能为null")
	private Integer id;
	@Min(5)
	private int age;



@Test
public void test02() throws UnsupportedEncodingException{
	Cat cat = new Cat();
	
	ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
	Validator validator = validatorFactory.getValidator();
	Set<ConstraintViolation<Cat>> validate = validator.validate(cat);
	
	for (ConstraintViolation<Cat> constraintViolation : validate) {
		System.out.println(new String(constraintViolation.getMessage().getBytes(),"UTF-8"));
	}
}


输出

最小不能小于5

id不能为null

 

注意:这里输出乱码是因为Hibernate自带的properties文件属于单字节编码IS0-8859-1,所以没办法显示中文,一个解决方法是将自带的文件拷贝一份重新载入,要不就自己手动转换输出编码。

官方文档:

https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#section-declaring-method-constraints

 

Spring 容器支持

入参校验

@Valid Goods goods, BindingResult result

 

通过result.hasErrors() 判断数据校验是否成功

 

也可以通过AOP的方式拦截提交请求进行参数校验


@Component
@Aspect
public class BindingResultAop {

    private final Logger LOG = LoggerFactory.getLogger(this.getClass());

    @Pointcut("execution(* com.yingjun.ssm.web.*.*(..))")
    public void aopMethod(){}

    @Around("aopMethod()")
    public Object  around(ProceedingJoinPoint joinPoint) throws Throwable{
        LOG.info("before method invoking!");
        BindingResult bindingResult = null;
        for(Object arg:joinPoint.getArgs()){
            if(arg instanceof BindingResult){
                bindingResult = (BindingResult) arg;
            }
        }
        if(bindingResult != null){
            if(bindingResult.hasErrors()){
                String errorInfo="["+bindingResult.getFieldError().getField()+"]"+bindingResult.getFieldError().getDefaultMessage();
                return new BaseResult<Object>(false, errorInfo);
            }
        }
        return joinPoint.proceed();
    }
}


 

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注