<h1 class="postTitle"><a id="cb_post_title_url" class="postTitle2" href="https://www.cnblogs.com/beiyan/p/5946345.html">Java Bean Validation 最佳实践</a></h1><div class="clear"></div><div class="postBody"><div id="cnblogs_post_body" class="blogpost-body"><p>参数校验是我们程序开发中必不可少的过程。用户在前端页面上填写表单时,前端js程序会校验参数的合法性,当数据到了后端,为了防止恶意操作,保持程序的健壮性,后端同样需要对数据进行校验。后端参数校验最简单的做法是直接在业务方法里面进行判断,当判断成功之后再继续往下执行。但这样带给我们的是代码的耦合,冗余。当我们多个地方需要校验时,我们就需要在每一个地方调用校验程序,导致代码很冗余,且不美观。</p>

那么如何优雅的对参数进行校验呢?JSR303就是为了解决这个问题出现的,本篇文章主要是介绍 JSR303,Hibernate Validator 等校验工具的使用,以及自定义校验注解的使用。

校验框架介绍

JSR303 是一套JavaBean参数校验的标准,它定义了很多常用的校验注解,我们可以直接将这些注解加在我们JavaBean的属性上面,就可以在需要校验的时候进行校验了。注解如下:

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

Spring validtor 同样扩展了jsr303,并实现了方法参数和返回值的校验

Spring 提供了MethodValidationPostProcessor类,用于对方法的校验

@NotEmpty,@NotNull和@NotBlank的区别

1 @NotEmpty :不能为null,且Size>0

2 @NotNull:不能为null,但可以为empty,没有Size的约束

3 @NotBlank:只用于String,不能为null且trim()之后size>0

代码实现

添加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)此处指定了注解的实现类为ListNotHasNullValidatorImplpublic @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;@Overridepublic 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元素")@Validprivate 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

@Beanpublic 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

转载于:https://www.cnblogs.com/zhuhui-site/p/10088417.html

Java Bean Validation 最佳实践相关推荐

  1. Spring Validation最佳实践

    Spring Validation最佳实践 前言 一.简单使用 二.使用步骤 1.引入依赖 2.requestBody 参数校验 (1)在 DTO对象的字段上声明约束注解: (2)在接口方法参数上声明 ...

  2. 基于Java Bean Validation对Request参数进行校验的设计思路

    数据校验是任何一个应用程序都会用到的功能,无论是显示层还是持久层. 通常,相同的校验逻辑会分散在各个层中, 这样,不仅浪费了时间还会导致重复代码的发生. 为了避免重复, 开发人员经常会把这些校验逻辑直 ...

  3. 编写高性能Java代码的最佳实践

    编写高性能Java代码的最佳实践 摘要:本文首先介绍了负载测试.基于APM工具的应用程序和服务器监控,随后介绍了编写高性能Java代码的一些最佳实践.最后研究了JVM特定的调优技巧.数据库端的优化和架 ...

  4. 高性能Java代码的最佳实践

    高性能Java代码的最佳实践 前言 在这篇文章中,我们将讨论几个有助于提升Java应用程序性能的方法.我们首先将介绍如何定义可度量的性能指标,然后看看有哪些工具可以用来度量和监控应用程序性能,以及确定 ...

  5. Java Bean Validation 详解

    前言 最近维护一个老项目,项目使用最原始的Servlet,项目中充斥着各种类似判空的简单校验,为了减少重复代码,因此需要手动引入 Java 的 Bean Validation. Java Bean V ...

  6. java查询枚举_查找Java枚举的最佳实践

    查找Java枚举的最佳实践 我们有一个REST API,客户端可以在其中提供表示Java Enums中服务器上定义的值的参数. 因此,我们可以提供一个描述性错误,我们将此valueOf(..)方法添加 ...

  7. bean validation校验方法参数_Spring Validation最佳实践及其实现原理,参数校验没那么简单!

    本文同名博客老炮说Java:https://www.laopaojava.com/,每天更新Spring/SpringMvc/SpringBoot/实战项目等文章资料 顺便再给大家推荐一套Spring ...

  8. Spring Validation最佳实践及其实现原理,参数校验没那么简单!

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:六点半起床 juejin.im/post/685654110 ...

  9. Spring Validation 最佳实践及其实现原理,参数校验没那么简单!

    之前也写过一篇关于Spring Validation使用的文章,不过自我感觉还是浮于表面,本次打算彻底搞懂Spring Validation.本文会详细介绍Spring Validation各种场景下 ...

最新文章

  1. 使用LINUX SENDMAIL发送邮件
  2. 不可错过的java面试博客之java集合篇
  3. 文章页点赞php代码,wordpress文章页面添加点赞功能
  4. Python Django session存取值代码演示及jsonpickle序列化
  5. JavaScript中十个一步拷贝数组的方法
  6. python输入一个整数列表 列表元素为18_Python-18 (高级变量1--列表)
  7. python 数组去重复_numpy数组去掉重复的行,保留唯一的行数据
  8. VB.NET工作笔记004---查看电脑已经安装了哪些COM组件,可以用个OleViewer.zip
  9. php table td边框,怎样对table和td技巧性设置表格边框
  10. matlab中dzdx,MatConvnet工具箱使用手册翻译理解一
  11. shell脚本快速执行命令
  12. python计算方位角_利用python计算太阳天顶角、方位角、高度角
  13. 马云背后的电商SaaS,到底谁在埋单?
  14. 没有了耳机接口,怎么让手机同时支持充电、听歌?USB-C音频方案了解一下
  15. 异常信息:由于内部错误,服务器无法处理该请求。有关该错误的详细信息,请打开服务器上的 IncludeExceptionDetailInFaults
  16. ProgressBar进度条(圆形进度条|水平进度条)
  17. 马化腾:移动互联网上半场接近尾声 腾讯将拥抱产业互联网
  18. 安装屏保软件(Linux终端演示 “黑客帝国” 字母雨界面)和Linux修改管理员密码
  19. 相容/不相容非齐次线性方程组的最小二乘解与最佳最小二乘解
  20. 如何巧妙的避开办公室的监(tou)控(kui)工具大势至而不被管理机发现

热门文章

  1. struts2校验再提交多条提示信息
  2. android事件分发 入口(dispatchTouchEvent)
  3. android log system
  4. 分布式服务常见问题—访问量统计如何做?
  5. Java IO流及应用(一)
  6. Vue - 在v-repeat中使用计算属性
  7. android系统action大全
  8. JavaScript 创建数组
  9. html Selection
  10. dos命令行输入adb shell命令为什么报错