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

Java validator整理的更多相关文章

  1. java笔记整理

    Java 笔记整理 包含内容     Unix Java 基础, 数据库(Oracle jdbc Hibernate pl/sql), web, JSP, Struts, Ajax Spring, E ...

  2. Java资料整理

    Java资料整理 原创 2017年08月25日 17:20:44 14211  1.LocalThread的应用场景,数据传输适合用LocalThread么 2.linux的基本命令    软链接.更 ...

  3. 自定义Java Validator

    自定义Java Validator 在项目中,针对汉字的长度计算,数据库和java的计算方式不一致,需要重新处理下java 的 Validator,使其满足项目 建立自定义的 validator an ...

  4. JAVA基础整理-集合篇(一)

    集合作为JAVA的基础知识,本来感觉自己理解的很清楚了,但是在最近的一次面试中还是答得不尽如人意!再次做一下整理,以便加深理解以及随时查阅. 首先,java.util包中三个重要的接口及特点:List ...

  5. Java数据结构整理(一)

    ava数据结构内容整理关键字: 数据结构 Collection:List.SetMap:HashMap.HashTable如何在它们之间选择一.Array , ArraysJava所有“存储及随机访问 ...

  6. 面试之Java知识整理

    1.面向对象都有哪些特性 继承.封装.多态性.抽象 2.Java中实现多态的机制是什么? 继承与接口 3.Java中异常分为哪些种类 3.1按照异常需要处理的时机分为编译时异常(CheckedExce ...

  7. Java细节整理——数组与内存控制

    重点:使用Java数组之前,必须对数组对象进行初始化. 当数组的所有元素都被分配了合适的内存空间,并指定了初始值时,数组的初始化完成.程序以后将不能重新改变数组对象在内存中的位置和大小. 知识点整理: ...

  8. java面试整理(会持续更新..)

    本人出道至今,经历了大大小小百余场战斗,,,下面整理的面试题有些有答案,有些没答案,那个谁说过:"要抱着怀疑的态度去编程,所以,即便有答案,也不一定正确,即便我本地正确,但是由于屏幕前的你和 ...

  9. Java基础整理

    一.Java中的遍历 1.在java开发中会碰到遍历List删除其中多个元素的情况,如果使用一般的for循环以及增强的for循环,代码会抛出异常ConcurrentModificationExcept ...

随机推荐

  1. C++学习笔记(十七):RTTI

    这个知识点被遗漏了,可以结合之前的这篇文章看类型转换这个知识点. RTTI(Run-Time Type Information,运行时类型信息)即程序能够使用基类的指针或引用来检查这些指针或引用所指的 ...

  2. Css基础-介绍及语法

    css 文件后缀.css 基础语法: selector { property:value } 例如: h1 {color:red;font-size:14px;} color:字体颜色  font-s ...

  3. js与java通信

    js 调用java中的接口并传递参数给客户端处理方式: webView.addJavascriptInterface(new NewsDetail() , "newsDetail" ...

  4. const和readonly差别

    我们都知道,const和static readonly的确非常像:通过类名而不是对象名进行訪问,在程序中仅仅读等等.在多数情况下能够混用.二者本质的差别在于,const的值是在编译期间确定的,因此仅仅 ...

  5. VK Cup 2015 - Finals, online mirror D. Restructuring Company 并查集

    D. Restructuring Company Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/5 ...

  6. SqlServer按照指定顺序对字段进行排序

    最近的一个项目,使用存储过程对报表进行分析,其中有一些名称需要根据指定顺序显示,而其名称对应的编号并不是按照要求的顺序排列的.通过上网查找资料,发现sql 中的charindex函数可以帮助解决这个问 ...

  7. IOS debug网络PonyDebugger 实践篇

    引言:  PonyDebugger是一个很给力的iOS调试工具,它的监视器安装在Chrome浏览器下做为插件使用,通过监视器和PonyDebugger的iOS SDK相辅相成,可以很好的监视App的运 ...

  8. Docker容器案例:应用 Mysql

    原创 杜亦舒   前阶段体验 Mysql 的新版本 5.7.13,由于机器里已经有 Mysql了,再安装另一个版本会有一些麻烦,为了简单,便使用 Docker 容器来安装 可能有人会认为没必要,在一台 ...

  9. Supervised Learning-Regression

    假设我们有一张房子属性及其价格之间的关系表(如下图所示) ,根据这些数据如何估计其他房子的价格?我们的第一个反应肯定是参考属性相似的房子的价格.在属性较少时这个方法还行得通,属性太复杂时就不那么简单了 ...

  10. js之createTextRange方法

    createTextRange()方法作用: 主要是用来对一些文本对象进行操作.比如你有一大段文字,都在同一个P标签内,但是你只希望通过JS改变其中的一小部分,这时就可以用createTextRang ...