Hibernate Validator是Java Validation API(JSR 303)标准的一个具体实现,用于对参数进行合法性校验。校验数据在任何应用中都是一个很常见的任务,所以JCP组织定义了一个标准来规范化这个任务操作,那就是Java Validation API。

先来探讨下如何在SpringMVC中集成Java Validation API实现Controller方法入参校验。可以说,所有的Java Web应用都会涉及到对Controller方法入参做校验,在不使用Java Validation API之前,做校验时有人是这样写的:

@Controller
@RequestMapping("/order")
public class OrderController {@ResponseBody@RequestMapping(value = "/getOrderDetails", method = RequestMethod.POST)public OrderDetailsVo getOrderDetails(@RequestBody JSONObject json) {String token = json.optString("token");Assert.hasLength(token,1101,"非法请求参数"); String orderCode = json.optString("orderCode");Assert.hasLength(orderCode,1101,"非法请求参数");// .......}
}

这种写法我认为有三个弊端:

  1. 用JSONObject来接收入参,取值非常麻烦,很容易就弄错字段名
  2. 如果入参非常复杂,用JSONObject的话,下一个接手的人就很难搞错清整个方法入参的JSON结构,可维护性极差
  3. 方法里校验逻辑啰嗦重复

下面来看看如何重构这个代码:

首先引入validator依赖,如果是springboot项目,那么引入此依赖:

<dependency><groupId>org.springframework.boot</group><artifactId>spring-boot-starter-validation</artifactId>
</dependency>

一般spring项目可引入此三个依赖:

<dependency><groupId>org.hibernate</groupId><artifactId>hibernate-validator</artifactId><version>5.4.1.Final</version>
</dependency>
<dependency><groupId>javax.el</groupId><artifactId>javax.el-api</artifactId><version>3.0.0</version>
</dependency>
<dependency><groupId>org.glassfish</groupId><artifactId>javax.el</artifactId><version>3.0.0</version>
</dependency>

好的写法应该是这样:

@RestController
@RequestMapping("/order")
public class OrderController {@RequestMapping(value = "/getOrderDetails", method = RequestMethod.POST)public OrderDetailsResVo getOrderDetails(@Validated @RequestBody OrderDetailsReqVo reqVo) {// .......}
}

其中OrderDetailsReqVo类定义:

public class OrderDetailsReqVo {@NotBlank // 此注解表明字段不能为null,也不能为空字符串private String token;@NotBlank // 此注解表明字段不能为null,也不能为空字符串private String orderCode;// get set......
}

添加@Validated就是提示Spring在将JSON入参转换为OrderDetailsReqVo对象后,对此对象进行校验。如果校验不通过Spring则会抛出MethodArgumentNotValidException异常。

在这里统一捕捉校验失败的MethodArgumentNotValidException异常:

// 不熟悉这些Spring注解的推荐去看《Spring实战》书籍
@ControllerAdvice
public class GlobalHandler {private final Logger logger = LoggerFactory.getLogger(GlobalHandler.class);/*** 所有的校验失败情况都会由这里处理*/@ResponseBody@ExceptionHandler(MethodArgumentNotValidException.class)public Result exceptionHandler(MethodArgumentNotValidException e) {Result result = new Result("1101","非法请求参数");logger.error("req params error", e);return result;}// ......
}

好,开始Java Validation API的学习,先看下都有哪些注解可用:

JSR 303提供的标准注解:

Hibernate 提供的额外注解:

我讲一些比较常用的,其它的就由各位同学自己探索吧

Car类定义:

public class Car {@NotBlank // 不能为null,不能为空字符串private String manufacturer;@NotNull // 不能为null@Size(min = 2, max = 14) // 字符串长度位于2到14之间private String licensePlate;@Min(2)@Max(5) // 注意,未添加NotNull注解,所以seatCount可以为null,只有当seatCount不为null@Min @Max才会做校验private Integer seatCount;@AssertTrue // registered不为null时,则值需是trueprivate Boolean registered;// groups用于指定所属的校验组@AssertTrue(message = "The car has to pass the vehicle inspection first", groups = CarChecks.class)private Boolean passedVehicleInspection;@Valid // 表明应对driver对象内字段继续做校验@NotNullprivate Driver driver;@Valid // 表明应对passengers里的Person对象内字段继续做校验@Size(max = 2) // 表明passengers最多只能有两个对象private List<Person> passengers = new ArrayList<>();private String brand;@Range(min = 2,max = 4) // 作用同@Min @Maxprivate Integer doors;private CarTypeEnum carTypeEnum;// get set......
}

Driver类定义:

public class Driver extends Person {@NotNull@Min(value = 18, message = "必须年满18岁", groups = DriverChecks.class)public Integer age;@NotNull@AssertTrue(message = "必须具有驾照", groups = DriverChecks.class)public Boolean hasDrivingLicense;// get set......
}

Person类定义:

public class Person {private long personId = 0;@NotBlank@CheckCase(value = CaseMode.UPPER, message = "名字必须为大写")private String name;public Address address;public Date birthday;// get set......
}

RentalCar类定义:

@GroupSequence({RentalChecks.class, CarChecks.class, RentalCar.class})
public class RentalCar extends Car {@AssertFalse(message = "The car is currently rented out", groups = RentalChecks.class)private boolean rentalStation = true;// get set......
}

注意各个类上添加的注解

测试代码:

public class ValidationTest {private static Validator validator = Validation.buildDefaultValidatorFactory().getValidator();@Testpublic void test() {// 可以试着给不同字段赋值查看校验效果Car car = new Car();car.setManufacturer("benz");car.setLicensePlate("234234");car.setSeatCount(5);car.setRegistered(true);car.setPassedVehicleInspection(true);Driver driver = new Driver();driver.setName("JACK");driver.setAge(11);driver.setHasDrivingLicense(false);car.setDriver(driver);car.setPassengers(new ArrayList<>());car.setBrand("");car.setDoors(4);car.setCarTypeEnum(CarTypeEnum.BENZ);// 不传递校验顺序,则只校验groups为Default的,没有显式在注解写明groups属性的,则默认为DefaultvalidateBean(car);// 校验顺序显式传递validateBean(car, OrederedChecks.class);car.getDriver().setAge(20);car.getDriver().setHasDrivingLicense(true);validateBean(car, OrederedChecks.class);car.setSeatCount(1);Set<ConstraintViolation<Car>> constraintViolations3 = validator.validateProperty(car, "seatCount");System.err.println(constraintViolations3.iterator().next().getMessage());Set<ConstraintViolation<Car>> constraintViolations4 = validator.validateValue(Car.class, "registered", false);System.err.println(constraintViolations4.iterator().next().getMessage());// 校验顺序写在bean的类注解上RentalCar rentalCar = new RentalCar();Set<ConstraintViolation<RentalCar>> constraintViolations5 = validator.validate(rentalCar);System.err.println(constraintViolations5.iterator().next().getMessage());}@Testpublic void test1() {// 测试自定义校验注解Person person = new Person("John");Set<ConstraintViolation<Person>> constraintViolations = validator.validate(person);ConstraintViolation<Person> constraintViolation = constraintViolations.iterator().next();System.out.println(constraintViolation.getMessage());}public static <T> void validateBean(T bean, Class<?>... groups) {Set<ConstraintViolation<T>> constraintViolations = validator.validate(bean, groups);if (constraintViolations.isEmpty()) {System.out.println("校验通过");return;}List<String> errors = new ArrayList<>(10);for (ConstraintViolation<T> constraintViolation : constraintViolations) {errors.add(constraintViolation.getPropertyPath() + constraintViolation.getMessage());}//throw new ValidationException(StringUtils.join(errors, ","));System.err.println(StringUtils.join(errors, ","));}
}

其中Person类@CheckCase是自定义的校验,也就是说我们可以自定义自己的校验逻辑,这里@CheckCase(value = CaseMode.UPPER, message ="名字必须为大写")就是说字段值必须为大写,那么要如何做呢?分三步:

第一步:定义校验注解

@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = CheckCaseValidator.class) //CheckCaseValidator类在下面
@Documented
public @interface CheckCase {String message() default "";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};CaseMode value();
}public enum CaseMode {UPPER,LOWER;
}

第二部:编写校验逻辑实现

public class CheckCaseValidator implements ConstraintValidator<CheckCase, String> {private CaseMode caseMode;@Overridepublic void initialize(CheckCase constraintAnnotation) {this.caseMode = constraintAnnotation.value();}@Overridepublic boolean isValid(String value, ConstraintValidatorContext context) {if (value == null) {return true;}if (caseMode == CaseMode.UPPER) {return value.equals(value.toUpperCase());} else {return value.equals(value.toLowerCase());}}
}

第三步:在bean添加注解即可生效

public class Person {private long personId = 0;@CheckCase(value = CaseMode.UPPER, message = "名字必须为大写")private String name;public Address address;public Date birthday;// ......
}

源码github地址:

jufeng98/java-master​github.com

sql2008安装时提示参数不能为空_Java Validation API,实现参数的合法性校验相关推荐

  1. sql2008安装时提示参数不能为空_PHP命令行脚本接收传入参数的三种方式

    通常PHP都做http方式请求了,可以使用GET or POST方式接收参数,有些时候需要在shell命令下把PHP当作脚本执行,比如定时任务.这就涉及到在shell命令下如何给php传参的问题,通常 ...

  2. 如何解决Office2016安装时提示:错误1406。安装程序无法将值写入注册表项\.xlsx

    写在这里的初衷,一是备忘,二是希望得到高人指点,三是希望能遇到志同道合的朋友. 目录 一.当前问题 二.解决办法 一.当前问题 Office2016安装时提示:错误1406.安装程序无法将值写入注册表 ...

  3. 打包解决方案后,安装时提示只能在IIS5.1以上运行解决方法

    打包解决方案后,安装时提示只能在IIS5.1以上运行解决方法 参考文章: (1)打包解决方案后,安装时提示只能在IIS5.1以上运行解决方法 (2)https://www.cnblogs.com/wp ...

  4. 解决robotframework安装时提示wxPython not found问题

    解决robotframework安装时提示wxPython not found问题 参考文章: (1)解决robotframework安装时提示wxPython not found问题 (2)http ...

  5. SqlServer在安装时提示:需要Microsoft.NET Framework 3.5 Service Pack 1 规则失败

    场景 SqlServer 2014 Enterprise 企业版安装程序下载与安装教程: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/detai ...

  6. 安装时提示用户在命令行上发出了EULAS_AGREED=1,表示不接受许可协议

    安装虚拟机安装不上,换了好几个版本,有的版本显示"安装时提示用户在命令行上发出了EULAS_AGREED=1,表示不接受许可协议",有的版本显示不访问网络位置信息. 以上出现的原因 ...

  7. Vmware 安装时提示directory ezboot not found解决办法

    Vmware 安装时提示directory ezboot not found 想了很久,终于在网上找到,有两个原因: 1.找不到硬盘: 2.操作系统ISO文件本身也有问题. 但是怎么验证这两个问题的存 ...

  8. Android studio2.3小米8.5.1不能安装应用,没有MIUI优化,打开USB安装时提示“请插入SIM卡”,安装时手机没有任何反应,studio报Installation failed w

    今天升级了MIUI后,Android studio2.3小米8.5.1不能安装应用,手机红米2a,没有MIUI优化,打开USB安装时提示"请插入SIM卡",安装时手机没有任何反应, ...

  9. Docker安装时提示Existing installation is up to date

    文章目录 问题描述: 解决方法: 问题描述: Docker御载不干净导致安装时提示如下图 解决方法: 删除注册表: HKLM\SOFTWARE\Microsoft\Windows\CurrentVer ...

最新文章

  1. 3维线程格 gpu_图形处理单元(GPU)
  2. 2个YUV视频拼接技术
  3. Acwing第 15 场周赛【未完结】
  4. 稀疏多项式的运算用链表_用漫画告诉你—什么是HashMap?
  5. Hibernate关于父类子类的映射
  6. AntiSamy测试
  7. commons-pool
  8. 基于金融知识图谱的会计欺诈风险识别方法
  9. Python3 爬虫(一)-- 简单网页抓取
  10. 使用SharedPreference保存用户数据的步骤
  11. matlab中方差分析的自由度,多因素方差分析的自由度
  12. iOS13免越狱修改微信提示音方法!亲测有用!
  13. 74ls20设计半加器_组合逻辑电路(半加器全加器及逻辑运算)实验报告
  14. PTA 7-55 剿灭魔教 (30分)(拓扑排序bfs版)
  15. 云服务器ecs是虚拟机,云服务器ecs虚拟机
  16. 课程学习方案——python(1)
  17. 云原生在京东丨揭秘五大云原生项目在京东的落地实践
  18. 对偶式与反函数_函数Y =A(B+C)的对偶式Y’= 和反函数`Y=
  19. 侍魂胧月传说服务器维护中,侍魂胧月传说手游4月8日停机维护更新公告
  20. 【教你区分】TVS二极管和稳压二极管的不同

热门文章

  1. 荣耀20青春版鸿蒙,荣耀20青春版曝光,浴霸三摄+麒麟810+系统亮点满满
  2. mysql数据漂移_第28问:SIP 漂移时,会影响正在使用的数据库连接么?
  3. 央视网报道“手机就能打到拖拉机”,网友直呼“想种地了”
  4. 冷链食品竟然也有 “身份证”?
  5. 首次公开!阿里巴巴云原生实时数仓核心技术揭秘
  6. H5移动端页面设计心得分享
  7. 用UE4创造开放世界:Kite 实时演示
  8. SQL中除数为0处理情况演示
  9. 一天学完spark的Scala基础语法教程八、集合(idea版本)
  10. spring boot配置文件【application.yml】常见问题之一,未添加空格