当前位置:   article > 正文

从Spring-Boot开始深入理解Spring系列(五)——Spring-Boot集成JavaEE的JSR380的BV2.0规范_springboot 整合jsr 380

springboot 整合jsr 380

基础概念

什么是JSR

Java Specification Requests的缩写,意思是Java 规范提案。 是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。 任何人都可以提交JSR,以向Java平台增添新的API和服务。

什么是BV

是一个java规范(准确来说,属于JavaEE范围内的规范)。

  • 允许您通过注释表达对象模型的约束
  • 允许您以可扩展的方式编写自定义约束
  • 提供API来验证对象和嵌套的(继承的)对象
  • 提供API以验证参数并返回方法和构造函数的值
  • 报告违规行为(本地化,个性化定制)
  • 在Java SE上运行,但集成在Java EE 6及更高版本中; Bean Validation 2.0是Java EE 8的一部分

BV的实现有哪些?

主流实现框架,有Hibernate validation、Spring validation等。而Spring 的validation 核心模块,首先是完整实现BV规范,JSR380。其次,在spring-mvc中实现了自动化的校验。并且基于hibernate validation进行了增强(并兼容hibernate validation的实现)

为什么需要BV?

不用的现状(必要性)

在java的世界中,没有BV之前的校验,依靠一些第三方的工具包提供的基础简单的校验,或者自行编码验证。工具包主要有Apache的common工具包中的validationUtils、Spring的validationUtils。
例一:

public class StandardValidation {

	public static void main(String[] args) {
		System.out.println(validationWithoutAnnotation(" ", -1));
	}

	public static String validationWithoutAnnotation(String inputString, Integer inputInt) {
		String error = null;
		if (null == inputString) {
			error = "inputString不能为null";
		} else if (null == inputInt) {
			error = "inputInt不能为null";
		} else if (1 > inputInt.compareTo(0)) {
			error = "inputInt必须大于0";
		} else if (inputString.isEmpty() || inputString.trim().isEmpty()) {
			error = "inputString不能为空字符串";
		} else {
			// DO
		}
		return error;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

例二:
Spring的自行开发的数据校验功能由3个部分组成:
校验器——Validator,他会运行校验代码。
校验对象,实际上就是一个JavaBean,Validator会对其进行校验。
校验结果——Errors,一次校验的结果都存放在Errors实例中。
这是Spring在Bean Validation规范制定之前就实现的数据校验功能,ValidationUtils的注释中@since标签是2003年5月6号,而JSR-303定稿时间已经是6年之后(2009年)的事了。

package chkui.springcore.example.hybrid.springvalidation.entity;
//车辆信息
public class Vehicle {
	private String name;
	private String type;
	private String engine;
	private String manufacturer;
	private Calendar productionDate; 

    /**Getter Setter*/
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

然后针对这个实体声明一个校验器。校验器要实现org.springframework.validation.Validator接口:

package chkui.springcore.example.hybrid.springvalidation.validator;

public class VehicleValidator implements Validator {
	private List<String> _TYPE = Arrays.asList(new String[] { "CAR", "SUV", "MPV" });

	public boolean supports(Class<?> clazz) {
        //将验证器和实体类进行绑定,如果这里返回false在验证过程中会抛出类型不匹配的异常
		return Vehicle.class.isAssignableFrom(clazz);
	}

	public void validate(Object target, Errors errors) { //验证数据
		Vehicle vehicle = Vehicle.class.cast(target);
		if (null == vehicle.getName()) {
            //使用验证工具绑定结果
			ValidationUtils.rejectIfEmpty(errors, "name", "name.empty", "车辆名称为空");
		}
		if (!_TYPE.contains(vehicle.getType())) {
            //向Error添加验证错误信息
			<2> errors.rejectValue("type", "type.error", "汽车类型必须是" + _TYPE);
		}
        //More validate ......
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

有了验证对象(JavaBean)和对应的验证器(Validator)就完成了一组验证功能。注意VehicleValidator::validate方法传递的errors参数,验证工具会将错误实例传递进来交给开发者去组装验证结果。

public class SpringValidationApp {
	private static void springValidation(ApplicationContext ctx) {
		VehicleValidator vehicleValidator = new VehicleValidator();//创建验证器
		Vehicle vehicle = new Vehicle();//创建验证对象
		<1> ValidationError error = new ValidationError("Vehicle");//创建错误信息
		ValidationUtils.invokeValidator(vehicleValidator, vehicle, error);//执行验证
		List<FieldError> list = error.getFieldErrors();
		int count = 1;
        //输出验证结果
		for(FieldError res : list) {
			print("Error Info ", count++ , ".");
			print("Entity:", res.getObjectName());
			print("Field:", res.getField());
			print("Code:", res.getCode());
			print("Message:", res.getDefaultMessage());
			print("-");
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

执行完毕后,ValidationError中记录了所有校验错误信息。错误信息分为4个部分:

验证的对象的名称:在执行验证器的代码中<1>部分创建错误对象时指定。Vehicle就是验证对象的名称。

错误的域、错误code和错误信息:每一个错误都有对应的域、错误编码以及错误信息,在验证器<2>位置的代码就是指定错误信息。

以上错误信息可以通过error.getFieldErrors();来获取。

使用的好处(意义)

使用后的代码对比:

public class Game {
	@NotNull //非空
	@Length(min=0, max=5) //字符串长度小于5,这个是一个Hibernate Validator增加的注解
	private String name;
	
	@NotNull
	private String description;
	
	@NotNull
	@Min(0) //最小值>=0
	@Max(10) //最大值<=10
	private int currentVersion; 
    //getter and setter…………
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

1、更精简的代码
2、标准规范化的接口,使得学习、理解、交流沟通的成本大大地降低
3、代码的复用性更好
4、更加适合做统一的异常处理
5、依赖标准API规范,所以没有框架或者工具的强依赖性,可随意更换规范的实现框架

BV的应用场景:

基础应用

  • 对同一个被校验对象,在不同的情况下,需要使用不同的校验逻辑(如:密码校验)
@size(min = 8,default.group.class)
@size(min = 12, admin.group.class)
private char[] password ……
  • 1
  • 2
  • 3
  • 对集合进行约束
private List<String> names;

@OneElements(constraint=@NotEmpty)
private List<String> names;

private List<@NotEmpty String> names;

private List<@Pattern(regexp="[a-zA-Z] * ") String> names;
……
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 对日期进行校验
  • 货币
  • 任何其他,你想要的校验场景(自定义的校验)……

高级应用

  • 定制错误信息
  • 定制校验的提示语级别
  • 嵌套的类或者对象
  • 在方法上进行校验(入参和返回值),实施契约式编程
  • 组合多个校验约束的注解(使用or 、and 等谓语进行任意组合)
  • 为分组的约束,制定校验的顺序

spring-boot中使用BV

本示例,提供的特性有:

  1. 统一异常处理

  2. 使用hibernate validation校验:

    2.1:简单校验
    2.2:分组校验
    2.3:定制错误信息
    2.4:自定义校验约束
    2.5:使用定制化的校验消息等级提示信息
    2.6:使用简单的组合约束
    2.7:在方法上,使用对返回值的校验、参数的校验(契约式编程)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

使用Hibernate validation

  1. 引入maven依赖
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
  • 1
  • 2
  • 3
  • 4
  1. 建立实体类,并使用约束注解
    /**
     * @NotNull://CharSequence, Collection, Map 和 Array 对象不能是 null, 但可以是空集(size = 0)。  
     * @NotEmpty://CharSequence, Collection, Map 和 Array 对象不能是 null 并且相关对象的 size 大于 0。  
     * @NotBlank://String 不是 null 且去除两端空白字符后的长度(trimmed length)大于 0。 
     */

    public Integer id;
    @NotBlank(message = "用户名不能为空")
    @Length(min = 6,max = 20,message = "用户名需要为 6 - 20 个字符")
    public String username;
    @NotNull(message = "年龄不能为空")
    public Integer age;
    @Email(message = "邮箱格式不正确")
    @NotBlank(message = "邮箱不能为空")
    public String email;
    @NotBlank(message = "手机号码不能为空")
    public String phoneNumber;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  1. 在spring-mvc 中使用validation
@PostMapping("/addByJson")
    @ResponseBody
    public ResultBean addByJson(@RequestBody @Valid User user) {
        userService.add(user);
        return ResultBean.success();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  1. 运行程序,访问请求,检验效果
    在这里插入图片描述
    在这里插入图片描述

本文源码下载

https://github.com/bill4j/spring-boot-course/tree/develop/spring-boot-beanvalidation-exceptionHandler

Beanvalidation官方网站

规范下载查阅

Hibernate Beanvalidation

http://hibernate.org/validator/documentation/

http://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#preface

扩展的Beanvalidation

https://github.com/nomemory/java-bean-validation-extension

在密码校验中使用Beanvalidation的示例

https://github.com/Baeldung/spring-security-registration/blob/master/src/main/java/org/baeldung/validation/PasswordConstraintValidator.java

hibernate validation 高级应用demo

https://github.com/hibernate/hibernate-demos/tree/master/hibernate-validator

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/155032?site
推荐阅读
相关标签
  

闽ICP备14008679号