两种方式实现Spring 业务验证

简介: 验证在任何时候都非常关键。考虑将数据验证作为业务逻辑开发有利也有弊,Spring 认为,验证不应该只在Web 端进行处理,在服务端也要进行相应的处理,可以防止脏数据存入数据库中,从而避免为运维同学和测试同学造成更大的困扰,因为数据造成的bug会更加难以发现,而且开发人员关注点也不会放在数据本身的问题上,所以做服务端的验证也是非常有必要的。考虑到上面这些问题,Spring 提供了两种主要类型的验证:

验证在任何时候都非常关键。考虑将数据验证作为业务逻辑开发有利也有弊,Spring 认为,验证不应该只在Web 端进行处理,在服务端也要进行相应的处理,可以防止脏数据存入数据库中,从而避免为运维同学和测试同学造成更大的困扰,因为数据造成的bug会更加难以发现,而且开发人员关注点也不会放在数据本身的问题上,所以做服务端的验证也是非常有必要的。考虑到上面这些问题,Spring 提供了两种主要类型的验证:

  • 一个是实现Validator 接口来创建自定义验证器,用于服务端数据校验。
  • 一种是通过Spring 对Bean Validation 支持实现的。

通过使用 Spring Validator 接口进行验证

Spring 提供 Validator 接口用于验证对象。Validator 接口通过使用 Errors 对象来工作,以便在验证时,验证器可以向 Errors 对象报告验证失败。下面是一个简单的 对象示例

publicclass Person {
    private String name;
    privateint age;
    // get and set...
}

下面一个例子为 Person 对象提供了一种验证方式,通过实现了 org.springframework.validation.Validator 接口 的两个方法:

  • supports(Class): 表示此 Validator 是否能够验证提供的类的实例
  • validate(Object, org.springframework.validation.Errors): 验证给定的对象,如果验证错误,则注册具有给定 Errors 对象。

实现一个 Validator 非常简单,而且Spring 也提供了 ValidationUtils 工具类帮助进行验证。下面是一个验证 Person 对象的例子:

@Component

publicclass PersonValidator implements Validator {
// 此 Validator 只验证 Person 实例
public boolean supports(Class clazz) {
return Person.class.equals(clazz);
}
public void validate(Object obj, Errors e) {
ValidationUtils.rejectIfEmpty(e, "name", "name.empty");
}
}

上面代码示例中的静态方法 rejectIfEmpty() 方法用于拒绝name属性,当name 属性是 null 或者是 空串的时候。查看 ValidationUtils 文档关于它能够提供的功能。

然后再来编写配置类 AppConfig:

@Configuration
@ComponentScan("com.spring.validation")
publicclass AppConfig {}

使用 @Configuration 注解声明此类为配置类(更多 @Configuration 的用法,请参照 原创 | 我被面试官给虐懵了,竟然是因为我不懂Spring中的@Configuration )

配置@ComponentScan 注解用于自动装配,默认是使用 basePackages 扫描指定包,字符串表示。

然后对上面的程序进行验证

publicclass SpringValidationApplicationTests {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
Person person = new Person();
person.setAge(18);
person.setName(null);
PersonValidator personValidator = applicationContext.getBean("personValidator", PersonValidator.class);
BeanPropertyBindingResult result = new BeanPropertyBindingResult(person,"cxuan");
ValidationUtils.invokeValidator(personValidator,person,result);
List<ObjectError> allErrors = result.getAllErrors();
allErrors.forEach(e-> System.out.println(e.getCode()));
}
}

因为是基于注解的配置,所以使用 AnnotationConfigApplicationContext上下文启动类,把配置类 AppConfig 当作参数,然后构建一个Person 类,为了测试验证有效性,把 name 设置为 null,然后通过上下问的 getBean 方法获得 personValidator 的实例,通过使用 BeanPropertyBindingResult 把 person 绑定为 cxuan 的名字,然后使用 ValidationUtils 工具类进行验证,最后把验证的结果进行检查。

上面程序经验证后的结果如下:

org.springframework.validation.ValidationUtils - Invoking validator [com.spring.validation.PersonValidator@37918c79] DEBUG org.springframework.validation.ValidationUtils - Validator found 1 errors name.empty

使用 Bean Validation 进行验证

从 Spring4 开始,就已经实现对 JSR-349 Bean Validation 的全面支持。Bean Validation API 在 javax.validation.constraints 包中以 Java 注解(例如 @NonNull) 形式定义了一组可用域对象的约束。

通过使用 Bean Validation API ,可以避免耦合到特定的验证服务提供程序。Spring 对 Bean Validation API 提供了无缝支持,主要使用一些注解进行验证,下面一起来看一下

定义对象属性上的验证约束

首先,将验证约束应用于域对象属性。使用maven 配置需要引入对应的依赖

<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.4.Final</version>
</dependency>

之后定义了一些实体类,使用 javax.validation.constraints 包中的注释进行标注

publicclass Singer {
@NotNull
@Size(min = 2,max = 60)
private String firstName;
private String lastName;
@NotNull
private Genre genre;
private Gender gender;
get and set...
}

对于 firstName ,定义了两个约束,第一个约束由 @NotNull 进行控制,它表示该值不能为空。此外,@Size注解控制着 firstName 的长度在 2 - 60 之间。@NotNull 还用于 genre 属性。下面是GenreGender 的枚举类

publicenum Genre {
POP("P"),
JAZZ("J"),
BLUES("B"),
COUNTRY("C");
private String code;
private Genre(String code){
this.code = code;
}
public String toString(){
returnthis.code;
}
}
publicenum Gender {
MALE("M"),
FEMALE("F");
private String code;
Gender(String code){
this.code = code;
}
@Override
public String toString() {
returnthis.code;
}
}

Genre 表示歌手所属的音乐类型,而 Gender 与音乐事业不相关,所以可以为空

在 Spring 中配置 Bean Validation 支持

为了在 Spring 的 ApplicationContext 中配置对 Bean Validation API 的支持,可以在Spring 的配置中定义一个 LocalValidatorFactoryBean 的 bean如下

@Configuration
@ComponentScan("com.spring.validation")
publicclass ValidationConfig {
@Bean
LocalValidatorFactoryBean validatorFactoryBean(){
returnnew LocalValidatorFactoryBean();
}
}

声明一个 LocalValidatorFactoryBean 的 bean 是必须的。默认情况下,Spring 会在类路径下搜索 Hibernate Validator库,验证它是否存在。

下面我们编写一个为 Singer 类提供验证服务的服务类

@Service
publicclass SingerValidationService {
@Autowired
private Validator validator;
public Set<ConstraintViolation<Singer>> validateSinger(Singer singer){
return validator.validate(singer);
}
}

注入一个 javax.validation.Validator 实例(请注意与 Spring 提供的 Validator 接口不同)。一旦定义了 LocalValidatorFactoryBean ,就可以在应用程序中的任意位置创建 Validator 的句柄。要在 POJO 上进行验证,需要调用 validator.validate 方法,验证结果以 ConstraintViolation<T> 接口的集合形式返回。下面是上面例子程序的验证

publicclass SpringBeanValidationTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ValidationConfig.class);
SingerValidationService singerBean = applicationContext.getBean(SingerValidationService.class);
Singer singer = new Singer();
singer.setFirstName("c");
singer.setLastName("xuan");
singer.setGenre(null);
singer.setGender(null);
validateSinger(singer,singerBean);
applicationContext.close();
}
private static void validateSinger(Singer singer,SingerValidationService singerValidationService){
Set<ConstraintViolation<Singer>> violationSet = singerValidationService.validateSinger(singer);
listViolations(violationSet);
}
private static void listViolations(Set<ConstraintViolation<Singer>> violations){
System.out.println("violations.size() = " + violations.size());
for(ConstraintViolation<Singer> violation : violations){
System.out.println("Validation error for property : " + violation.getPropertyPath());
System.out.println("with value : " + violation.getInvalidValue());
System.out.println("with error message : " + violation.getMessage());
}
}
}

上述代码构建了一个 Singer 类进行验证,因为 firstname 属性的要求是长度介于 2 - 60 之间并且不能为null,所以这里只用了一个字符验证,genre 属性不能为null,最核心的验证方法就是 singerValidationService.validateSinger(singer).方法,它会调用

public Set<ConstraintViolation<Singer>> validateSinger(Singer singer){
return validator.validate(singer);
}

进行验证,验证的结果返回的是 ConstraintViolation<Singler>类型,然后把对应的错误信息输出,上面的错误信息是

violations.size() = 2 Validation error for property : firstName with value : c with error message : 个数必须在2和60之间 Validation error for property : genre with value : null with error message : 不能为null

可以打印出两个错误,并输出错误的属性、值以及错误信息。


            </div>
目录
相关文章
|
Prometheus 数据可视化 Cloud Native
Grafana 系列文章(四):Grafana Explore
Grafana 系列文章(四):Grafana Explore
Grafana 系列文章(四):Grafana Explore
|
安全 算法 文件存储
共享资料下载,自动转PDF并添加隐形水印
云盒子企业网盘增强文件安全,支持下载时自动转PDF并加水印。管理员可配置目录规则,选择明水印、隐形水印或点阵水印。明水印直观防复制,隐形水印用于隐蔽追踪,点阵水印不影响阅读。文件格式支持度和水印类型取决于设置。此功能适用于文档安全、版权保护等场景。欲知详情或测试,访问[云盒子官网](yhz66.com)咨询客服。
445 1
|
11月前
|
移动开发 JSON API
1688 商品详情数据接口(H5、APP 端)
1688商品详情数据接口是1688平台提供的数据交互通道,支持H5和APP端,提供商品的全面信息(如标题、价格、库存、销量等),并实时更新。开发者可通过HTTP/HTTPS协议调用接口,使用GET或POST方法获取数据。示例代码展示了如何用Python请求该接口,需替换API密钥和商品ID。
|
安全 算法 Linux
Linux 服务器还有漏洞?建议使用 OpenVAS 日常检查!
在数字化时代,Linux 服务器的安全至关重要。OpenVAS 是一款优秀的开源漏洞扫描工具,可以帮助及时发现并修复服务器中的安全隐患。本文将介绍 OpenVAS 的主要功能、使用方法及应对漏洞的措施,帮助用户加强服务器安全管理,确保企业数字化安全。
432 7
|
传感器 存储 物联网
单片机的原理与应用
单片机是一种将计算机的CPU、存储器、输入输出接口等功能集成在一块芯片上的微型计算机,被广泛应用于各类控制系统和智能设备中。
733 5
|
数据采集 监控 搜索推荐
数据收集方法
数据收集方法
610 2
|
运维 Kubernetes 调度
在K8S中,Pod亲和性和反亲和性作用区别有哪些?该如何使用?
在K8S中,Pod亲和性和反亲和性作用区别有哪些?该如何使用?
|
存储 缓存 安全
学习服务器硬件基础知识
服务器是指一种高性能计算机,提供计算、存储和通信服务。通常运行在网络环境中,为计算机、设备或用户提供资源共享、数据存储和处理等服务。服务器可以是专门设计的硬件设备,也可以是在普通计算机上运行的特定软件。
765 6
|
前端开发 数据安全/隐私保护
应用架构设计 - 智能物流柜
应用架构设计 - 智能物流柜
194 1
|
算法
Raid5数据恢复—Raid5算法简介&raid5磁盘阵列数据恢复案例
Raid5算法也被称为“异或运算”。异或是一个数学运算符,它应用于逻辑运算。异或的数学符号为“⊕”,计算机符号为“xor”。异或的运算法则为:a⊕b = (¬a ∧ b) ∨ (a ∧¬b)。如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。 异或也叫半加运算,其运算法则相当于不带进位的二进制加法。二进制下用1表示真,0表示假。异或的运算法则为:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0(同为0,异为1),这些法则与加法是相同的,只是不带进位。 异或略称为XOR、EOR、EX-OR,程序中有三种演算子:XOR、xor、⊕。使用方法如下z = x ⊕ y z
Raid5数据恢复—Raid5算法简介&raid5磁盘阵列数据恢复案例