前言
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
。
看到又有版本更新了,是不是很开心?但是,这还不够。因为还有更高版本!学习的速度永远赶不上变化的速度啊!Hibernate Validator实现已经更新到7.0.1.Final
,对应的Jakarta Bean Validation规范版本为3.0.0
。
而且,Jakarta Bean Validation的2.0.2
和3.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:
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.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
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.validator
和org.springframework.validation.Validator
的实现。
4.1.1、javax.validation.validator
4.1.2、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