Jakarta Bean Validation,Constrain once, validate everywhere!

前言

Jakarta Bean Validation到底是什么?我们不妨先看看其官网的相关介绍。

Bean Validation 2.0 is a spec!
It is done — after one year of hard work, and a bit more than four years after the previous revision, the final release of Bean Validation 2.0 (JSR 380) is out!

Last week, the JSR passed the Final Approval Ballot by the executive committee of the JCP unanimously with 25 “Yes” votes. After the ballot we’ve released version 2.0.0.Final of the specification, the API as well as the TCK. At the same time, the final version of the reference implementation, Hibernate Validator 6, was released, too.

Within the next few days, the final specification will also be available on the JSR 380 page on jcp.org, after which the work on this JSR is complete and the expert group officially will disband.

Posted by Gunnar Morling | 07 Aug 2017
参考网址:https://beanvalidation.org/news/2017/08/07/bean-validation-2-0-is-a-spec/

由上可知,Jakarta Bean Validation是JCP通过的JSR380规范,在2017年8月7日,Bean Validation 2.0最终版本已经发布,参考该规范的Hibernate Validator 6实现最终版本也已发布。

一句话总结:“Jakarta Bean Validation是规范,Hibernate Validator是该约束的一种实现”。

截至今日(2021年4月1日,愚人节),Hibernate Validator 6实现已经更新到6.2.0.Final,对应的Jakarta Bean Validation规范版本为2.0.2

jakarta.validation-api-2.0.2.jar
看到又有版本更新了,是不是很开心?但是,这还不够。因为还有更高版本!学习的速度永远赶不上变化的速度啊!Hibernate Validator实现已经更新到7.0.1.Final,对应的Jakarta Bean Validation规范版本为3.0.0

jakarta.validation-api-3.0.0.jar
而且,Jakarta Bean Validation的2.0.23.0.0差异还是蛮大的,不仅仅是3比2大了1个版本号,简直就不是一个东西!


1、Jakarta Bean Validation 2

pox.xml

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

package和class
Jakarta Bean Validation 2


2、Jakarta Bean Validation 3

pox.xml

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

package和class

Jakarta Bean Validation 3
注意,在Jakarta Bean Validation 3中,包名是jakarta.validation开头。而在Jakarta Bean Validation 2中,包名是javax.validation开头。


3、Hibernate Validator

Hibernate Validator,The Bean Validation reference implementation.
Express validation rules in a standardized way using annotation-based constraints and benefit from transparent integration with a wide variety of frameworks.
–Hibernate Validator官网
翻译:Hibernate Validator是Bean Validation的参考实现。
使用基于注解的约束以标准化的方式表达验证规则,并从与各种框架的透明集成中获益。

3.1、Constraints

hibernate-validator 6.2.0.Final
hibernate-validator 7.0.1.Final

3.2、验证实践

3.2.1、pom.xml

本次实践中我们以Hibernate Validator 6.2.0.Final为基准版本进行pom.xml依赖配置,然后,再不需要任何其它配置,即可启动项目,Service部分输出参考如下:

...
Connected to server
[2021-04-02 12:58:37,580] Artifact SpringMvc0401Validation:war: Artifact is being deployed, please wait...
INFO [2021/04/02 00:58:39.105] [RMI TCP Connection(3)-127.0.0.1]:  
	org.springframework.web.context.ContextLoader 
	Root WebApplicationContext: initialization started 
INFO [2021/04/02 00:58:39.605] [RMI TCP Connection(3)-127.0.0.1]:  
	org.springframework.web.context.ContextLoader 
	Root WebApplicationContext initialized in 485 ms 
INFO [2021/04/02 00:58:39.716] [RMI TCP Connection(3)-127.0.0.1]:  
	org.springframework.web.servlet.DispatcherServlet 
	Initializing Servlet 'springmvc' 
INFO [2021/04/02 00:58:40.335] [RMI TCP Connection(3)-127.0.0.1]:  
	com.company.project.controller.HelloJspController 
	正在创建HelloJspController 
INFO [2021/04/02 00:58:40.815] [RMI TCP Connection(3)-127.0.0.1]:  
	org.hibernate.validator.internal.util.Version 
	HV000001: Hibernate Validator 6.0.18.Final 
INFO [2021/04/02 00:58:41.696] [RMI TCP Connection(3)-127.0.0.1]:  
	org.springframework.web.servlet.DispatcherServlet 
	Completed initialization in 1980 ms 
[2021-04-02 12:58:41,722] Artifact SpringMvc0401Validation:war: Artifact is deployed successfully
[2021-04-02 12:58:41,722] Artifact SpringMvc0401Validation:war: Deploy took 4,142 milliseconds
...

在上述输出日志中,有非常关键的一句输出,说明Hibernate Validator已随着项目成功启动:

INFO [2021/04/02 00:58:40.815] [RMI TCP Connection(3)-127.0.0.1]:  
	org.hibernate.validator.internal.util.Version 
	HV000001: Hibernate Validator 6.0.18.Final 

3.2.2、Model

约束都被定义在javax.validation.constraints包,我们在Model的被验证字段上以注解方式直接添加即可,代码参考如下:

import javax.validation.constraints.*;

public class UserInfoModel {

	//必须是0到100之间
	@Min(value = 0, message = "成绩最小值为{value}")
	@Max(value = 100, message = "成绩最大值为{value}")
	private Integer score;

	//手机号码不为空
	@NotBlank(message = "手机号不能为空")
	@Pattern(regexp = "^1[3,4,5,6,7,8,9]\\d{9}$", message = "手机号码不正确")
	private String phone;

	//用户名 不为空
	@NotEmpty(message = "用户名不能为空NotEmpty")
	@NotBlank(message = "用户名不能为空")
	@Size(min = 6, message = "名称至少6个字符")
	private String name;
	
	...Getter\Setter\hashCode\equals\toString

}

3.2.3、Controller

Controller在进行验证和处理验证结果时,需要遵循如下规则:

  • 在控制器方法上要有Model和BindingResult 类型的入参;
  • 在Model参数增加@Valid注解;
  • 通过BindingResult 获取验证错误的数量和信息;
  • 通过ModelAndView 将错误信息传递给视图;

代码实现参考如下:


import com.company.project.model.UserInfoModel;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.validation.Valid;
//import jakarta.validation.Valid;

@Controller
@RequestMapping(value = "/user-info")
public class UserInfoController {

	@RequestMapping("/mothod01")
	public ModelAndView mothod01(@Valid UserInfoModel userInfoModel,
								 BindingResult bindingResult) {
		ModelAndView modelAndView = new ModelAndView();
		modelAndView.setViewName("/validator");
		int errorCount = bindingResult.getErrorCount();
		if (errorCount > 0) {
			FieldError score = bindingResult.getFieldError("score");
			FieldError phone = bindingResult.getFieldError("phone");
			FieldError name = bindingResult.getFieldError("name");
			if (score != null) {
				modelAndView.addObject("scoremsg", score.getDefaultMessage());
			}
			if (phone != null) {
				modelAndView.addObject("phonemsg", phone.getDefaultMessage());
			}
			if (name != null) {
				modelAndView.addObject("namemsg", name.getDefaultMessage());
			}
			modelAndView.setViewName("/validator");
		}

		return modelAndView;

	}
}

3.2.4、View

3.2.4.1、表单页面
<%--
  Created by IntelliJ IDEA.
  User: goldenunion@qq.com
  Date: 2021/3/24
  Time: 16:26
  To change this template use File | Settings | File Templates.
--%>


<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta name="author" content="goldenunion@qq.com"/>
    <meta name="Keywords" content="WWW,HTML,CSS,Javascript,XML,Json,JavaEE,Jsp,SpringMvc"/>
    <meta name="Description" content="该文档演示了JSP文档的基本结构。"/>
    <title>Title</title>
</head>
<body bgcolor="deepskyblue">

<h1>数据校验</h1>
<form action="${pageContext.request.contextPath}/user-info/mothod01" method="post">
    成绩:<input name="score" /> <span>${scoremsg }</span><br/><br/>
    姓名:<input name="name"/><span>${namemsg }</span><br/><br/>
    电话:<input name="phone"/><span>${phonemsg }</span><br/><br/>
    <input type="submit" value="注册"/>
</form>


</body>
</html>
3.2.4.2、显示页面

在jsp中,使用EL表达式通过对象名称取出其值,简单参考如下:

${scoremsg} <br/>

${phonemsg} <br/>

${namemsg} <br/>

页面运行效果:

手机号码不正确
名称至少6个字符

4、SpringMVC

4.1、LocalValidatorFactoryBean

LocalValidatorFactoryBean的完整包路径为

org.springframework.validation.beanvalidation.LocalValidatorFactoryBean。它是javax.validation.validatororg.springframework.validation.Validator的实现。

4.1.1、javax.validation.validator

javax.validation.validator

4.1.2、org.springframework.validation.Validator

org.springframework.validation.Validator

4.2、org.springframework.validation.Validator

package org.springframework.validation;

/**
 * A validator for application-specific objects.
 *
 * <p>This interface is totally divorced from any infrastructure
 * or context; that is to say it is not coupled to validating
 * only objects in the web tier, the data-access tier, or the
 * whatever-tier. As such it is amenable to being used in any layer
 * of an application, and supports the encapsulation of validation
 * logic as a first-class citizen in its own right.
 *
 * <p>Find below a simple but complete {@code Validator}
 * implementation, which validates that the various {@link String}
 * properties of a {@code UserLogin} instance are not empty
 * (that is they are not {@code null} and do not consist
 * wholly of whitespace), and that any password that is present is
 * at least {@code 'MINIMUM_PASSWORD_LENGTH'} characters in length.
 *
 * <pre class="code"> public class UserLoginValidator implements Validator {
 *
 *    private static final int MINIMUM_PASSWORD_LENGTH = 6;
 *
 *    public boolean supports(Class clazz) {
 *       return UserLogin.class.isAssignableFrom(clazz);
 *    }
 *
 *    public void validate(Object target, Errors errors) {
 *       ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userName", "field.required");
 *       ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "field.required");
 *       UserLogin login = (UserLogin) target;
 *       if (login.getPassword() != null
 *             && login.getPassword().trim().length() < MINIMUM_PASSWORD_LENGTH) {
 *          errors.rejectValue("password", "field.min.length",
 *                new Object[]{Integer.valueOf(MINIMUM_PASSWORD_LENGTH)},
 *                "The password must be at least [" + MINIMUM_PASSWORD_LENGTH + "] characters in length.");
 *       }
 *    }
 * }</pre>
 *
 * <p>See also the Spring reference manual for a fuller discussion of
 * the {@code Validator} interface and its role in an enterprise
 * application.
 *
 * @author Rod Johnson
 * @see SmartValidator
 * @see Errors
 * @see ValidationUtils
 */
public interface Validator {

	/**
	 * Can this {@link Validator} {@link #validate(Object, Errors) validate}
	 * instances of the supplied {@code clazz}?
	 * <p>This method is <i>typically</i> implemented like so:
	 * <pre class="code">return Foo.class.isAssignableFrom(clazz);</pre>
	 * (Where {@code Foo} is the class (or superclass) of the actual
	 * object instance that is to be {@link #validate(Object, Errors) validated}.)
	 * @param clazz the {@link Class} that this {@link Validator} is
	 * being asked if it can {@link #validate(Object, Errors) validate}
	 * @return {@code true} if this {@link Validator} can indeed
	 * {@link #validate(Object, Errors) validate} instances of the
	 * supplied {@code clazz}
	 */
	boolean supports(Class<?> clazz);

	/**
	 * Validate the supplied {@code target} object, which must be
	 * of a {@link Class} for which the {@link #supports(Class)} method
	 * typically has (or would) return {@code true}.
	 * <p>The supplied {@link Errors errors} instance can be used to report
	 * any resulting validation errors.
	 * @param target the object that is to be validated
	 * @param errors contextual state about the validation process
	 * @see ValidationUtils
	 */
	void validate(Object target, Errors errors);

}


5、JSR303

变化太快,如果没有老项目牵挂,是该痛痛快快和JSR303说分手了!


6、参考来源

  • Jakarta Bean Validation官网:https://beanvalidation.org/

  • Hibernate官网:http://hibernate.org/validator/

  • Spring官网:https://docs.spring.io

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AT阿宝哥

给作者送颗薄荷糖吧!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值