【Spring6】数据校验:Validation

10、数据校验:Validation

在这里插入图片描述

10.1、Spring Validation概述

在这里插入图片描述

在开发中,我们经常遇到参数校验的需求,比如用户注册的时候,要校验用户名不能为空、用户名长度不超过20个字符、手机号是合法的手机号格式等等。如果使用普通方式,我们会把校验的代码和真正的业务处理逻辑耦合在一起,而且如果未来要新增一种校验逻辑也需要在修改多个地方。而spring validation允许通过注解的方式来定义对象校验规则,把校验和业务逻辑分离开,让代码编写更加方便。Spring Validation其实就是对Hibernate Validator进一步的封装,方便在Spring中使用。

在Spring中有多种校验的方式

第一种是通过实现org.springframework.validation.Validator接口,然后在代码中调用这个类

第二种是按照Bean Validation方式来进行校验,即通过注解的方式。

第三种是基于方法实现校验

除此之外,还可以实现自定义校验

10.2、实验一:通过Validator接口实现

第一步 创建子模块 spring6-validator

在这里插入图片描述

第二步 引入相关依赖

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

    <dependency>
        <groupId>org.glassfish</groupId>
        <artifactId>jakarta.el</artifactId>
        <version>4.0.1</version>
    </dependency>
</dependencies>

第三步 创建实体类,定义属性和方法

package com.atguigu.spring6.validation.method1;

public class Person {
    private String name;
    private int age;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

第四步 创建类实现Validator接口,实现接口方法指定校验规则

package com.atguigu.spring6.validation.method1;

import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

public class PersonValidator implements Validator {

    @Override
    public boolean supports(Class<?> clazz) {
        return Person.class.equals(clazz);
    }

    @Override
    public void validate(Object object, Errors errors) {
        ValidationUtils.rejectIfEmpty(errors, "name", "name.empty");
        Person p = (Person) object;
        if (p.getAge() < 0) {
            errors.rejectValue("age", "error value < 0");
        } else if (p.getAge() > 110) {
            errors.rejectValue("age", "error value too old");
        }
    }
}

上面定义的类,其实就是实现接口中对应的方法,

supports方法用来表示此校验用在哪个类型上,

validate是设置校验逻辑的地点,其中ValidationUtils,是Spring封装的校验工具类,帮助快速实现校验。

第五步 使用上述Validator进行测试

package com.atguigu.spring6.validation.method1;

import org.springframework.validation.BindingResult;
import org.springframework.validation.DataBinder;

public class TestMethod1 {

    public static void main(String[] args) {
        //创建person对象
        Person person = new Person();
        person.setName("lucy");
        person.setAge(-1);
        
        // 创建Person对应的DataBinder
        DataBinder binder = new DataBinder(person);

        // 设置校验
        binder.setValidator(new PersonValidator());

        // 由于Person对象中的属性为空,所以校验不通过
        binder.validate();

        //输出结果
        BindingResult results = binder.getBindingResult();
        System.out.println(results.getAllErrors());
    }
}

10.3、实验二:Bean Validation注解实现

使用Bean Validation校验方式,就是如何将Bean Validation需要使用的javax.validation.ValidatorFactory 和javax.validation.Validator注入到容器中。spring默认有一个实现类LocalValidatorFactoryBean,它实现了上面Bean Validation中的接口,并且也实现了org.springframework.validation.Validator接口。

第一步 创建配置类,配置LocalValidatorFactoryBean

@Configuration
@ComponentScan("com.atguigu.spring6.validation.method2")
public class ValidationConfig {

    @Bean
    public LocalValidatorFactoryBean validator() {
        return new LocalValidatorFactoryBean();
    }
}

第二步 创建实体类,使用注解定义校验规则

package com.atguigu.spring6.validation.method2;

import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;

public class User {

    @NotNull
    private String name;

    @Min(0)
    @Max(120)
    private int age;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

常用注解说明
@NotNull 限制必须不为null
@NotEmpty 只作用于字符串类型,字符串不为空,并且长度不为0
@NotBlank 只作用于字符串类型,字符串不为空,并且trim()后不为空串
@DecimalMax(value) 限制必须为一个不大于指定值的数字,并且类型为BigDecimal
@DecimalMin(value) 限制必须为一个不小于指定值的数字,并且类型为BigDecimal
@Max(value) 限制必须为一个不大于指定值的数字
@Min(value) 限制必须为一个不小于指定值的数字
@Pattern(value) 限制必须符合指定的正则表达式
@Size(max,min) 限制字符长度必须在min到max之间
@Email 验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式

第三步 使用两种不同的校验器实现

(1)使用jakarta.validation.Validator校验

package com.atguigu.spring6.validation.method2;

import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Set;

@Service
public class MyService1 {

    @Autowired
    private Validator validator;

    public  boolean validator(User user){
        Set<ConstraintViolation<User>> sets =  validator.validate(user);
        return sets.isEmpty();
    }

}

(2)使用org.springframework.validation.Validator校验

package com.atguigu.spring6.validation.method2;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.BindException;
import org.springframework.validation.Validator;

@Service
public class MyService2 {

    @Autowired
    private Validator validator;

    public boolean validaPersonByValidator(User user) {
        BindException bindException = new BindException(user, user.getName());
        validator.validate(user, bindException);
        return bindException.hasErrors();
    }
}

第四步 测试

package com.atguigu.spring6.validation.method2;

import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestMethod2 {

    @Test
    public void testMyService1() {
        ApplicationContext context = new AnnotationConfigApplicationContext(ValidationConfig.class);
        MyService1 myService = context.getBean(MyService1.class);
        User user = new User();
        user.setAge(-1);
        boolean validator = myService.validator(user);
        System.out.println(validator);
    }

    @Test
    public void testMyService2() {
        ApplicationContext context = new AnnotationConfigApplicationContext(ValidationConfig.class);
        MyService2 myService = context.getBean(MyService2.class);
        User user = new User();
        user.setName("lucy");
        user.setAge(130);
        user.setAge(-1);
        boolean validator = myService.validaPersonByValidator(user);
        System.out.println(validator);
    }
}

10.4、实验三:基于方法实现校验

第一步 创建配置类,配置MethodValidationPostProcessor

package com.atguigu.spring6.validation.method3;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;

@Configuration
@ComponentScan("com.atguigu.spring6.validation.method3")
public class ValidationConfig {

    @Bean
    public MethodValidationPostProcessor validationPostProcessor() {
        return new MethodValidationPostProcessor();
    }
}

第二步 创建实体类,使用注解设置校验规则

package com.atguigu.spring6.validation.method3;

import jakarta.validation.constraints.*;

public class User {

    @NotNull
    private String name;

    @Min(0)
    @Max(120)
    private int age;

    @Pattern(regexp = "^1(3|4|5|7|8)\\d{9}$",message = "手机号码格式错误")
    @NotBlank(message = "手机号码不能为空")
    private String phone;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }
}

第三步 定义Service类,通过注解操作对象

package com.atguigu.spring6.validation.method3;

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

@Service
@Validated
public class MyService {
    
    public String testParams(@NotNull @Valid User user) {
        return user.toString();
    }

}

第四步 测试

package com.atguigu.spring6.validation.method3;

import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestMethod3 {

    @Test
    public void testMyService1() {
        ApplicationContext context = new AnnotationConfigApplicationContext(ValidationConfig.class);
        MyService myService = context.getBean(MyService.class);
        User user = new User();
        user.setAge(-1);
        myService.testParams(user);
    }
}

10.5、实验四:实现自定义校验

第一步 自定义校验注解

package com.atguigu.spring6.validation.method4;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {CannotBlankValidator.class})
public @interface CannotBlank {
    //默认错误消息
    String message() default "不能包含空格";

    //分组
    Class<?>[] groups() default {};

    //负载
    Class<? extends Payload>[] payload() default {};

    //指定多个时使用
    @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @interface List {
        CannotBlank[] value();
    }
}

第二步 编写真正的校验类

package com.atguigu.spring6.validation.method4;

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

public class CannotBlankValidator implements ConstraintValidator<CannotBlank, String> {

        @Override
        public void initialize(CannotBlank constraintAnnotation) {
        }

        @Override
        public boolean isValid(String value, ConstraintValidatorContext context) {
                //null时不进行校验
                if (value != null && value.contains(" ")) {
                        //获取默认提示信息
                        String defaultConstraintMessageTemplate = context.getDefaultConstraintMessageTemplate();
                        System.out.println("default message :" + defaultConstraintMessageTemplate);
                        //禁用默认提示信息
                        context.disableDefaultConstraintViolation();
                        //设置提示语
                        context.buildConstraintViolationWithTemplate("can not contains blank").addConstraintViolation();
                        return false;
                }
                return true;
        }
}

本文章参考B站 尚硅谷Spring零基础视频教程,2023版spring6入门到精通,仅供个人学习使用,部分内容为本人自己见解,与尚硅谷无关。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/8243.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

TenserRT(三)PYTORCH 转 ONNX 详解

第三章&#xff1a;PyTorch 转 ONNX 详解 — mmdeploy 0.12.0 文档 torch.onnx — PyTorch 2.0 documentation torch.onnx.export 细解 计算图导出方法 TorchScript是一种序列化和优化PyTorch模型的格式&#xff0c;将torch.nn.Module模型转换为TorchScript的torch.jit.Scr…

unicloud 模糊查询解决方案

序 1、where和aggregate的模糊搜索 2、第一种是“你好”去匹配“你好啊大家” 3、第二种是“家啊”去匹配“啊&#xff01;你家呢” 只要有1个字匹配就匹配 4、第三种是“家啊”去匹配“啊&#xff01;你家呢” 必须有“家”又有“啊”才匹配” 想看效果&#xff0c;大家可以自…

ROBOGUIDE教程:FANUC机器人摆焊焊接功能介绍与虚拟仿真操作方法

目录 摆焊功能简介 摆焊指令介绍 摆焊功能设置 摆焊条件设置 机器人摆焊示教编程 仿真运行 摆焊功能简介 使用FANCU机器人进行弧焊焊接时&#xff0c;也可以实现摆动焊接&#xff08;简称摆焊&#xff09;。 摆焊功能是在机器人弧焊焊接时&#xff0c;焊枪面对焊接方向…

面试字节,三面HR天坑,想不到自己也会阴沟里翻船....

阎王易见&#xff0c;小鬼难缠。我一直相信这个世界上好人居多&#xff0c;但是也没想到自己也会在阴沟里翻船。我感觉自己被字节跳动的HR坑了。 在这里&#xff0c;我只想告诫大家&#xff0c;offer一定要拿到自己的手里才是真的&#xff0c;口头offer都是不牢靠的&#xff0…

【CE】Mac下的CE教程Tutorial:进阶篇(第8关:多级指针)

▒ 目录 ▒&#x1f6eb; 导读开发环境1️⃣ 第8关&#xff1a;多级指针翻译操作验证其它方案&#x1f6ec; 文章小结&#x1f4d6; 参考资料&#x1f6eb; 导读 开发环境 版本号描述文章日期2023-03-操作系统MacOS Big Sur 11.5Cheat Engine7.4.3 1️⃣ 第8关&#xff1a;多…

MySQL数据库中的函数怎样使用?

函数 是指一段可以直接被另一段程序调用的程序或代码。 也就意味着&#xff0c;这一段程序或代码在MySQL中已经给我们提供了&#xff0c;我们要做的就是在合适的业务场景调用对应的函数完成对应的业务需求即可。 那么&#xff0c;函数到底在哪儿使用呢? 我们先来看两个场景&a…

【FPGA-Spirit_V2】基于FPGA的循迹小车-小精灵V2开发板

&#x1f389;欢迎来到FPGA专栏~基于FPGA的循迹小车 ☆* o(≧▽≦)o *☆嗨~我是小夏与酒&#x1f379; ✨博客主页&#xff1a;小夏与酒的博客 &#x1f388;该系列文章专栏&#xff1a;FPGA学习之旅 文章作者技术和水平有限&#xff0c;如果文中出现错误&#xff0c;希望大家能…

Android下载apk并安装apk(用于软件版本升级用途)

软件版本更新是每个应用必不可少的功能&#xff0c;基本实现方案是请求服务器最新的版本号与本地的版本号对比&#xff0c;有新版本则下载apk并执行安装。请求服务器版本号与本地对比很容易&#xff0c;本文就不过多讲解&#xff0c;主要讲解下载apk到安装apk的内容。 一、所需…

Socket套接字编程(实现TCP和UDP的通信)

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了 博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点!人生格言&#xff1a;当你的才华撑不起你的野心的时候,你就应该静下心来学习! 欢迎志同道合的朋友一起加油喔&#x1f9be;&am…

(链表)移除链表元素(双指针法)

文章目录前言&#xff1a;问题描述&#xff1a;解题思路&#xff08;双指针法&#xff09;&#xff1a;代码实现&#xff1a;总结&#xff1a;前言&#xff1a; 此篇是针对链表的经典练习题。 问题描述&#xff1a; 给你一个链表的头节点 head 和一个整数 val &#xff0c;请…

Js:apply/call/bind、作用域/闭包、this指向(普通,箭头,JS/Vue的this)

目录1、apply/call/bind2、作用域、作用域链和闭包核心1、预处理&#xff08;解析阶段&#xff09;——JS执行“代码段”之前2、生成执行上下文环境——对代码段(全局/函数体)进行处理3、执行上下文环境小结4、多个执行上下文环境5、作用域6、作用域和执行上下文7、从【自由变量…

小米万兆路由器里的 Docker 安装 Gitea

小米万兆路由器里的 Docker 安装 Gitea准备工作创建存储查看Docker Hub镜像信息拉取 gitea 镜像和运行容器配置通过 ssh 访问(Optional)其他小米2022年12月份发布了万兆路由器&#xff0c;里面可以使用Docker。 今天尝试在小米的万兆路由器里安装Gitea。 准备工作 先将一块US…

Java企业级开发学习笔记(2.1)MyBatis实现简单查询

该文章主要为完成实训任务&#xff0c;详细实现过程及结果见【http://t.csdn.cn/zi0wB】 文章目录零、创建数据库与表一、基于配置文件方式使用MyBatis基本使用1.1 创建Maven项目 - MyBatisDemo1.2 在pom文件里添加相应的依赖1.3 创建与用户表对应的用户实体类 - User1.4 创建用…

没有他们,人工智能只能死翘翘

我过去写过一篇文章《很多所谓伟大的贡献&#xff0c;其实都是狗屎运》&#xff0c;今天我也写写人工智能。&#xff08;1&#xff09;人才深度神经网络如果不从明斯基和罗森布拉特说起&#xff0c;那就应该可以从1965年Ivakhnenko发明前馈神经网络说起。但关键里程碑是出自Rum…

SpringBoot2核心功能 --- 原理解析

一、Profile功能 为了方便多环境适配&#xff0c;springboot简化了profile功能。 1.1、application-profile功能 默认配置文件 application.yaml&#xff1b;任何时候都会加载指定环境配置文件 application-{env}.yaml激活指定环境配置文件激活 命令行激活&#xff1a;java -…

【快乐手撕LeetCode题解系列】—— 环形链表 II

【快乐手撕LeetCode题解系列】—— 环形链表 II&#x1f60e;前言&#x1f64c;环形链表 II&#x1f64c;画图分析&#xff1a;&#x1f60d;思路分析&#xff1a;&#x1f60d;源代码分享&#xff1a;&#x1f60d;总结撒花&#x1f49e;&#x1f60e;博客昵称&#xff1a;博客…

STM32与Python上位机通过USB虚拟串口通信

文章目录前言1. 查看原理图2. 新建工程3.添加代码与烧录4. python代码编写总结问题解决思路前言 在详细阅读广大网友的教程之后&#xff0c;我对STM32和Python通过USB通信的流程烂熟于心。 尝试用ST公司的NUCLEO-L476RG板子进行简单的回环通信测试&#xff0c;发现还是存在网上…

Linux·异步IO编程框架

hi&#xff0c;大家好&#xff0c;今天分享一篇Linux异步IO编程框架文章&#xff0c;对比IO复用的epoll框架&#xff0c;到底性能提高多少&#xff1f;让我们看一看。 译者序 本文组合翻译了以下两篇文章的干货部分&#xff0c;作为 io_uring 相关的入门参考&#xff1a; Ho…

【RocketMQ】顺序消息实现原理

全局有序 在RocketMQ中&#xff0c;如果使消息全局有序&#xff0c;可以为Topic设置一个消息队列&#xff0c;使用一个生产者单线程发送数据&#xff0c;消费者端也使用单线程进行消费&#xff0c;从而保证消息的全局有序&#xff0c;但是这种方式效率低&#xff0c;一般不使用…

Web 攻防之业务安全:接口未授权访问/调用测试(敏感信息泄露)

Web 攻防之业务安全&#xff1a;接口未授权访问/调用测试 业务安全是指保护业务系统免受安全威胁的措施或手段。广义的业务安全应包括业务运行的软硬件平台&#xff08;操作系统、数据库&#xff0c;中间件等&#xff09;、业务系统自身&#xff08;软件或设备&#xff09;、业…