Java Bean Validation 最佳实践
参数校验是我们程序开发中必不可少的过程。用户在前端页面上填写表单时,前端js程序会校验参数的合法性,当数据到了后端,为了防止恶意操作,保持程序的健壮性,后端同样需要对数据进行校验。后端参数校验最简单的做法是直接在业务方法里面进行判断,当判断成功之后再继续往下执行。但这样带给我们的是代码的耦合,冗余。当我们多个地方需要校验时,我们就需要在每一个地方调用校验程序,导致代码很冗余,且不美观。
那么如何优雅的对参数进行校验呢?JSR303就是为了解决这个问题出现的,本篇文章主要是介绍 JSR303,Hibernate Validator 等校验工具的使用,以及自定义校验注解的使用。
校验框架介绍
JSR303 是一套JavaBean参数校验的标准,它定义了很多常用的校验注解,我们可以直接将这些注解加在我们JavaBean的属性上面,就可以在需要校验的时候进行校验了。注解如下:

Hibernate validator 在JSR303的基础上对校验注解进行了扩展,扩展注解如下:

Spring validtor 同样扩展了jsr303,并实现了方法参数和返回值的校验
Spring 提供了MethodValidationPostProcessor类,用于对方法的校验
代码实现
添加JAR包依赖
在pom.xml中添加如下依赖:
<!--jsr 303-->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<!-- hibernate validator-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.0.Final</version>
</dependency>
最简单的参数校验
1、Model 中添加校验注解
public class Book {
private long id;
/**
* 书名
*/
@NotEmpty(message = "书名不能为空")
private String bookName;
/**
* ISBN号
*/
@NotNull(message = "ISBN号不能为空")
private String bookIsbn;
/**
* 单价
*/
@DecimalMin(value = "0.1",message = "单价最低为0.1")
private double price; // getter setter ....... }
2、在controller中使用此校验
/**
* 添加Book对象
* @param book
*/
@RequestMapping(value = "/book", method = RequestMethod.POST)
public void addBook(@RequestBody @Valid Book book) {
System.out.println(book.toString());
}
当访问这个post接口时,如果参数不符合Model中定义的话,程序中就回抛出400异常,并提示错误信息。
自定义校验注解
虽然jSR303和Hibernate Validtor 已经提供了很多校验注解,但是当面对复杂参数校验时,还是不能满足我们的要求,这时候我们就需要 自定义校验注解。
下面以“List数组中不能含有null元素”为实例自定义校验注解
1、注解定义如下:
package com.beiyan.validate.annotation; import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*; import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME; /**
* 自定义参数校验注解
* 校验 List 集合中是否有null 元素
*/ @Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = ListNotHasNullValidatorImpl.class)////此处指定了注解的实现类为ListNotHasNullValidatorImpl public @interface ListNotHasNull { /**
* 添加value属性,可以作为校验时的条件,若不需要,可去掉此处定义
*/
int value() default 0; String message() default "List集合中不能含有null元素"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; /**
* 定义List,为了让Bean的一个属性上可以添加多套规则
*/
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
@interface List {
ListNotHasNull[] value();
}
}
2、注解实现类:
package com.beiyan.validate.annotation; import org.springframework.stereotype.Service; import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.List; /**
* 自定义注解ListNotHasNull 的实现类
* 用于判断List集合中是否含有null元素
*/ @Service
public class ListNotHasNullValidatorImpl implements ConstraintValidator<ListNotHasNull, List> { private int value; @Override
public void initialize(ListNotHasNull constraintAnnotation) {
//传入value 值,可以在校验中使用
this.value = constraintAnnotation.value();
} public boolean isValid(List list, ConstraintValidatorContext constraintValidatorContext) {
for (Object object : list) {
if (object == null) {
//如果List集合中含有Null元素,校验失败
return false;
}
}
return true;
} }
3、model添加注解:
public class User {
//其他参数 .......
/**
* 所拥有的书籍列表
*/
@NotEmpty(message = "所拥有书籍不能为空")
@ListNotHasNull(message = "List 中不能含有null元素")
@Valid
private List<Book> books;
//getter setter 方法.......
}
使用方法同上,在在需要校验的Model上面加上@Valid 即可
分组验证
对同一个Model,我们在增加和修改时对参数的校验也是不一样的,这个时候我们就需要定义分组验证,步骤如下
1、定义两个空接口,分别代表Person对象的增加校验规则和修改校验规则
/**
* 可以在一个Model上面添加多套参数验证规则,此接口定义添加Person模型新增时的参数校验规则
*/
public interface PersonAddView {
} /**
* 可以在一个Model上面添加多套参数验证规则,此接口定义添加Person模型修改时的参数校验规则
*/
public interface PersonModifyView {
}
2、Model上添加注解时使用指明所述的分组
public class Person {
private long id;
/**
* 添加groups 属性,说明只在特定的验证规则里面起作用,不加则表示在使用Deafault规则时起作用
*/
@NotNull(groups = {PersonAddView.class, PersonModifyView.class}, message = "添加、修改用户时名字不能为空", payload = ValidateErrorLevel.Info.class)
@ListNotHasNull.List({
@ListNotHasNull(groups = {PersonAddView.class}, message = "添加上Name不能为空"),
@ListNotHasNull(groups = {PersonModifyView.class}, message = "修改时Name不能为空")})
private String name;
@NotNull(groups = {PersonAddView.class}, message = "添加用户时地址不能为空")
private String address;
@Min(value = 18, groups = {PersonAddView.class}, message = "姓名不能低于18岁")
@Max(value = 30, groups = {PersonModifyView.class}, message = "姓名不能超过30岁")
private int age;
//getter setter 方法......
}
3、启用校验
此时启用校验和之前的不同,需要指明启用哪一组规则
/**
* 添加一个Person对象
* 此处启用PersonAddView 这个验证规则
* 备注:此处@Validated(PersonAddView.class) 表示使用PersonAndView这套校验规则,若使用@Valid 则表示使用默认校验规则,
* 若两个规则同时加上去,则只有第一套起作用
*/
@RequestMapping(value = "/person", method = RequestMethod.POST)
public void addPerson(@RequestBody @Validated({PersonAddView.class, Default.class}) Person person) {
System.out.println(person.toString());
} /**
* 修改Person对象
* 此处启用PersonModifyView 这个验证规则
*/
@RequestMapping(value = "/person", method = RequestMethod.PUT)
public void modifyPerson(@RequestBody @Validated(value = {PersonModifyView.class}) Person person) {
System.out.println(person.toString());
}
Spring validator 方法级别的校验
JSR和Hibernate validator的校验只能对Object的属性进行校验,不能对单个的参数进行校验,spring 在此基础上进行了扩展,添加了MethodValidationPostProcessor拦截器,可以实现对方法参数的校验,实现如下:
1、实例化MethodValidationPostProcessor
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
2、在所要实现方法参数校验的类上面添加@Validated,如下
@RestController
@Validated
public class ValidateController {
}
3、在方法上面添加校验规则:
@RequestMapping(value = "/test", method = RequestMethod.GET)
public String paramCheck(@Length(min = 10) @RequestParam String name) {
System.out.println(name);
return null;
}
当方法上面的参数校验失败,spring 框架就回抛出异常
{
"timestamp": 1476108200558,
"status": 500,
"error": "Internal Server Error",
"exception": "javax.validation.ConstraintViolationException",
"message": "No message available",
"path": "/test"
}
从此可以优雅的对参数进行校验了
写在后面的话:
本篇文章只列举了常用的几种校验方法,其实关于校验的内容还有很多:
校验信息的国际化显示,
组合参数校验,
message中使用EL表达式,
将校验信息绑定到ModelAndView等,这里就不一一列出了,下面这几篇文章写的也不错,读者可以参考:
将校验信息绑定到ModelAndView http://www.voidcn.com/blog/983836259/article/p-5794496.html
集成Bean Validation 1.1(JSR-349)到SpringMVC https://my.oschina.net/qjx1208/blog/200946
本文的全部代码已上传开源中国git仓库: http://git.oschina.net/beiyan/Validate
Java Bean Validation 最佳实践的更多相关文章
- 深入了解数据校验:Java Bean Validation 2.0(JSR380)
每篇一句 吾皇一日不退役,尔等都是臣子 相关阅读 [小家Java]深入了解数据校验(Bean Validation):基础类打点(ValidationProvider.ConstraintDescri ...
- Java bean validation 规范与参考实现
1.Apache Bval 依赖包:validation-api-1.1.0.Final.jar org.apache.bval.bundle-1.1.1.jar bval-core-1.1.1.ja ...
- paip.复制文件 文件操作 api的设计uapi java python php 最佳实践
paip.复制文件 文件操作 api的设计uapi java python php 最佳实践 =====uapi copy() =====java的无,要自己写... ====php copy ...
- Java 网络编程最佳实践(转载)
http://yihongwei.com/2015/09/remoting-practice/ Java 网络编程最佳实践 Sep 10, 2015 | [Java, Network] 1. 通信层 ...
- 避免Java中NullPointerException的Java技巧和最佳实践
Java中的NullPointerException是我们最经常遇到的异常了,那我们到底应该如何在编写代码是防患于未然呢.下面我们就从几个方面来入手,解决这个棘手的问题吧. 值得庆幸的是,通过应用 ...
- 使用DataStax Java驱动程序的最佳实践
引言 如果您想开始建立自己的基于Cassandra的Java程序,欢迎! 也许您已经参加过我们精彩的DataStax Academy课程或开发者大会,又或者仔细阅读过Cassandra Java驱动的 ...
- Java Bean Validation(参数校验) 最佳实践
转载来自:http://www.cnblogs.com 参数校验是我们程序开发中必不可少的过程.用户在前端页面上填写表单时,前端js程序会校验参数的合法性,当数据到了后端,为了防止恶意操作,保持程序的 ...
- Spring Validation最佳实践及其实现原理,参数校验没那么简单!
之前也写过一篇关于Spring Validation使用的文章,不过自我感觉还是浮于表面,本次打算彻底搞懂Spring Validation.本文会详细介绍Spring Validation各种场景下 ...
- java 导出 excel 最佳实践,java 大文件 excel 避免OOM(内存溢出) excel 工具框架
产品需求 产品经理需要导出一个页面的所有的信息到 EXCEL 文件. 需求分析 对于 excel 导出,是一个很常见的需求. 最常见的解决方案就是使用 poi 直接同步导出一个 excel 文件. 客 ...
随机推荐
- 关于IIF多个条件的用法
IIF(yyzt = 0, '生产', IIF(yyzt = 10,'入库',IIF(yyzt=20,'销售经理',IIF(yyzt=30,'用户','其他'))))
- jquery使用注意点以及建议
jquery是一个非常优秀的js框架,相信大部分人都用过,也都非常熟悉它的应用,用起来也非常简单,基本的操作也都不用说了.这里不在罗列jquery的api,总结下需要注意的点,都是平时容易犯错误的地方 ...
- Android 基于Android的手机邮件收发(JavaMail)之三(邮件接收)
初次做这个程序的时候,是仿照着网上别人的程序做的.因为本人比较菜,是一个新手,以前的基础知识没有学好,所以尽管有了别人的代码但是还是不知道怎么在界面上显示出它的效果来,废话不多少,现在就贴出我的参考程 ...
- iOS XCode启用/关闭Clang Warnings
前言:warnings是编码中很重要的一个环节,编译器给出合理的warning能帮助开发者找到自己代码的问题,防止很多bug产生. 默认用XCode创建一个工程,会自动开启一些重要的warnings ...
- asp.net 自带的缓存
本文导读:在.NET运用中经常用到缓存(Cache)对象.有HttpContext.Current.Cache以及HttpRuntime.Cache,HttpRuntime.Cache是应用程序级别的 ...
- ios移动端部分手机不支持background-attachment: fixed 的解决办法
ios系统和某些移动端background-attachment:fixed不兼容性,没有任何效果,但可以hack一下就可以了,代码如下: ps:想在哪个标签加背景,可以在它class后:before ...
- 替换jenkins上打包完成的安装包的方法
修改 /root/.jenkins/jobs/Item名称/lastSuccessful/archive/*/obj 文件夹下的文件,就可以在下图二中下载新修改的文件,需要注意的是,此文件夹下不能有新 ...
- listener does not currently know of SID项目部署报数据库错
百度以后是数据库配置错误啊,但是我觉得就是对的呀,也去验证过了. 反正知道问题就是在databaseurl那里,但是原因是什么呢?其他地方部署都是好的呀! 集群问题啊,数据库人员采用集群方式配置数据库 ...
- 滚动轮播效果,.net 没得混看来只能去写js 了
<!DOCTYPE html> <html> <head> <title> 滚动图片 </title> <style type=&qu ...
- 我的第二个FluentNHibernate例子with Knockout
在上一篇我的第一个FluentNHibernate例子的基础上,我们用上knockoutjs 1用nuget添加knockoutjs包 2用nuget添加json.net包 3..在Index.csh ...