Spring中的数据校验---JSR303
介绍–什么是JSR303
JSR 303是Java中的一项规范,用于定义在Java应用程序中执行数据校验的元数据模型和API。JSR 303的官方名称是"Bean Validation",它提供了一种在Java对象级别上执行验证的方式,通常用于确保输入数据的完整性和准确性。
JSR 303中最常见的用法是使用注解在Java Bean上添加验证规则。以下是一些常用的注解:
 此实现与 Hibernate ORM 没有任何关系。 JSR 303 用于对 Java Bean 中的字段的值进行验证。
 Spring MVC 3.x 之中也大力支持 JSR-303,可以在控制器中对表单提交的数据方便地验证。
 注:可以使用注解的方式进行验证
JSR 303 基本的校验规则
空检查
- @Null 验证对象是否为null
 - @NotNull 验证对象是否不为null, 无法查检长度为0的字符串
 - @NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0, 只对字符串, 且会去掉前后空格.
 - @NotEmpty 检查约束元素是否为NULL或者是EMPTY.
 
Booelan检查
- @AssertTrue 验证 Boolean 对象是否为 true
 - @AssertFalse 验证 Boolean 对象是否为 false
 
其他校验
@Size(min, max): 检查值的长度是否在指定范围内。
 @Min(value): 检查数字值是否大于等于指定值。
 @Max(value): 检查数字值是否小于等于指定值。
 @Pattern(regex): 使用正则表达式检查字符串值。
 @Email: 检查字符串是否为有效的电子邮件地址等。
在实体类或者vo类使用验证规则,可以大幅度减轻数据校验的规范性价比。
具体使用
添加依赖
   <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency><dependency><groupId>jakarta.servlet</groupId><artifactId>jakarta.servlet-api</artifactId><scope>provided</scope></dependency>
 
对需要使用校验的字段添加注解
信息可以进行传值设置,不然就是默认值
@NotEmpty(message = "品牌名必须填写")@Schema(description = "品牌名")private String name;
 
默认信息如下
 
在控制层开启校验
在控制层开启该注解@Validated即可开启校验
   public Result<String> update(@Validated @RequestBody PmsBrandVO vo){pmsBrandService.update(vo);return Result.ok();}
 
自定义校验失败
上诉的步骤中,校验失败,不满足条件的会抛出异常,所以为了和前端配合需要进行自定义异常处理,返回前端一个json,而不是服务端抛出异常
 BindException
 在需要返回json的校验bean后跟,BindingResult,校验绑定结果类,对异常进行处理
    public Result<String> save(@Validated({AddGroup.class}) @RequestBody PmsBrandVO vo, BindingResult result){
//
// 形参单个添加这个可以,但是批量很难实现,为此需要自定义异常处理
// 需要取求掉该注解,才可以将异常抛出if (result.hasErrors()){String message = result.getFieldErrors().stream().map(fieldError -> {// 获取到错误字段String field = fieldError.getField();// 获取到自定义的错误消息提示String errMessage = fieldError.getDefaultMessage();// 返回拼接的错误消息字符串return field + ":" + errMessage;}).collect(Collectors.joining(", ")); // 将错误消息用逗号分隔
log.info("错误消息:{}",message);
// 创建一个 Result 对象,将错误消息传递给它return Result.error(message);}else {pmsBrandService.save(vo);return Result.ok();}
 
但是上诉只样也只是对单个控制器校验校验为此,需要自己,定义异常处理结果
自定义异常处理器 处理校验失败异常
1.把控制层的结构异常结果绑定类进行删除,让控制器将异常进行抛出
 2.自定义异常处理
/*** 异常处理器* 用于集中处理所有异常情况,并确保返回给前端的是处理过的 JSON 信息而不是异常信息。*/@Slf4j
@RestControllerAdvice//监听rescontroller的增强方法 advice增强 对应还有controllelrAdvice
public class ServerExceptionHandler {/*** 处理自定义异常* @param ex 抛出的自定义异常* @return 包含异常信息的 Result 对象*/@ExceptionHandler(ServerException.class)//捕获的异常类型public Result<String> handleException(ServerException ex) {return Result.error(ex.getCode(), ex.getMsg());}/*** 处理 Spring MVC 参数绑定、Validator 校验不正确的异常* @param ex 抛出的绑定异常* @return 包含异常信息的 Result 对象*/@ExceptionHandler(BindException.class)public Result<String> bindException(BindException ex) {FieldError fieldError = ex.getFieldError();assert fieldError != null;return Result.error(fieldError.getDefaultMessage());}/*** 处理访问被拒绝的异常* @param ex 抛出的访问被拒绝异常* @return 包含异常信息的 Result 对象*/@ExceptionHandler(AccessDeniedException.class)public Result<String> handleAccessDeniedException(Exception ex) {return Result.error(ErrorCode.FORBIDDEN);}/*** 未知异常类型,用于处理未捕获的其他异常情况*/@ExceptionHandler(Exception.class)public Result<String> handleException(Exception ex) {log.error(ex.getMessage(), ex);return Result.error(ErrorCode.INTERNAL_SERVER_ERROR);}}
 
这样就可以做到统一处理,异常结果
高级功能
自定义校验注解
如果现有的异常处理结果满足不了我们对字段的需求,那么可以进行自定义校验注解,在自定义校验注解之前,我们需要了解自定义校验注解
自定义注解
注解有些元注解,以及生命周期都是很简单的概念,这里讲一下大致
 自定义注解主要通过@interface关键字来定义。
 自定义注解的组成包括:
注解声明:使用@interface关键字。
 元注解(Meta-annotations):
 用于注解其他注解的注解。常用的元注解有@Target、@Retention、- @Documented和@Inherited。
- @Target:指定注解可以应用的Java元素类型(如METHOD, FIELD等)。
 - @Retention:指定注解在哪一个级别可用,生命周期(源代码中(SOURCE)、类文件中(CLASS)或运行时(RUNTIME)),而我们定义的大部分注解都是在运行时候,用来进行操作日志保存和权限校验
注解体:定义注解的属性。
其他的注解关键字,点开任意注解都可以了解大概 
演示
我这里定义自定义注解,模拟操作前进行的日志保存
 首先启动开启注解功能
@EnableAspectJAutoProxy
 
/***  所有的Annotation 会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是接口。*/
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Prelog {String message() default "执行前先打印的日志信息";
}
 
在我的业务控制器上添加注解
 
好了现在我的注解定义好了,并且让我的方法使用上了注解,但是这样注解是没办法知道我们的业务逻辑的,
 所以需要实现他的逻辑,这里运用到了aop详细了解aop思想,大概就是对目标做增强,在不改变源码的基础上
引入aop的依赖
 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>
 
定义一个切面类,对我自定义注解进行逻辑实现
@Aspect
@Component
@Slf4j
public class PrelogAspect {//切点:使用LogAnnotation注解标识的方法都进行切入,也可以使用通配符配置具体要切入的方法名@Pointcut("@annotation(com.mall.Annotation.Prelog)")public void pointCut(){}//环绕通知/*** 在AOP中,joinPoint.proceed()方法用于继续执行切入点处的原始方法。换句话说,它实际上调用了被切入的方法,无论是类的构造函数、方法或字段初始化等。* 在你的情况下,你的切点是使用@Prelog注解标识的方法,因此当切点匹配到一个被@Prelog注解标记的方法时,joinPoint.proceed()方法会执行该方法。因此,在@Around通知中,joinPoint.proceed()执行的就是被@Prelog注解标记的方法。* 所以,Object jsonResult = joinPoint.proceed();这行代码实际上执行了被@Prelog注解标记的方法,并将其结果存储在jsonResult变量中。这个变量可以在切面中进一步处理或返回给调用方。* @param joinPoint* @return* @throws Throwable*/@Around("pointCut()")public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {try {System.out.println("开始执行注解逻辑");// 获取目标方法签名MethodSignature signature = (MethodSignature) joinPoint.getSignature();// 获取目标方法Method method = signature.getMethod();// 获取注解值Prelog annotation = method.getAnnotation(Prelog.class);// 获取属性String message = annotation.message();System.out.println(message);// 执行目标方法Object jsonResult = joinPoint.proceed(); // 执行方法return jsonResult;} catch (Exception e) {e.printStackTrace();throw e; // 抛出异常}}}
 
访问被标记注解的接口
 
当然自定义注解配合aop还可以做权限校验,访问接口前判断是否有对应权限
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;@Aspect
@Component
public class PermissionAspect {@Around("@annotation(CheckPermission)")public Object checkPermission(ProceedingJoinPoint joinPoint) throws Throwable {MethodSignature signature = (MethodSignature) joinPoint.getSignature();CheckPermission annotation = signature.getMethod().getAnnotation(CheckPermission.class);String permission = annotation.value();// 这里模拟权限校验逻辑if (!hasPermission(permission)) {throw new SecurityException("没有权限执行此操作");}return joinPoint.proceed(); // 执行原方法}private boolean hasPermission(String permission) {/***根据该用户在系统的上下文 对照是否拥有该权限**/// 模拟权限校验逻辑,实际中应替换为具体的校验逻辑// 例如,检查当前用户是否拥有该权限// 这里简单模拟总是返回truereturn true;}
}
 
好了大概了解自定义注解和aop的原理进行实现,自定义校验数据的注解
自定义校验注解实现
1.定义校验注解 我这里定义的是判断字段的值是否是我设置的集合中的值
/*** 1.编写一个自定义注解作用于多个元素校验*/
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {OptionListConstraint.class })//3.指定使用什么校验器 可以指定多个校验器
public @interface OptionList {/*** 1.1改造validate注解的基本属性* 注解的类型只能是基本类型和布尔值以及枚举,字符串* @return*///2.定义自己需要的注解属性int[] values()   default  {0,1};//默认该注解的这个属性是0,1String message() default "必须提交指定的数值";//原min注解的默认消息定义在租界中华这个Class<?>[] groups() default { };
//做校验的时候自定义负载参数Class<? extends Payload>[] payload() default { };
}
 
2.对注解进行实现
 jsr中的注解进行注解校验都是通过实现校验器接口实现的,点开@Constrain注解
 
所以需要实现该接口
/*** 2.对校验器进行重写* 实现该接口的俩个方法* 参数<校验注解,校验对象类型>*     就是自定义注解中的逻辑实现*/
public class OptionListConstraint implements ConstraintValidator<OptionList, Integer> {private Set<Integer> set= new HashSet<Integer>();//初始化方法// 参数为校验注解// 可以在该方法中获取校验注解中的属性值@Overridepublic void initialize(OptionList constraintAnnotation) {//1.得到赋于注解的数值int[] values = constraintAnnotation.values();//2.将数值赋值给setfor (int i : values) {set.add(i);}}/*** 判断是否校验成功* @param value object to validate 需要校验的对象 也就是赋值的属性字段的值* @param context context in which the constraint is evaluated 上下文对象** @return*/@Overridepublic boolean isValid(Integer value, ConstraintValidatorContext context) {// 判断当前的值是否在set集合中return set.contains(value);}
}
 
校验分组
这个行为类似于范围限制,比如,对于id主键字段,我们在新增时候是需要前端不传递的,修改又需要前端传递,为此对与不同状态进行分组处理
- 定义俩个接口 表示不同组别 无需写什么方法

2.限定字段校验范围,那些字段是什么组别的时候进行校验

3.控制层 对应校验赋值时标明组别 注意注解是@Validated 不是@Valid

 
相关文章:
Spring中的数据校验---JSR303
介绍–什么是JSR303 JSR 303是Java中的一项规范,用于定义在Java应用程序中执行数据校验的元数据模型和API。JSR 303的官方名称是"Bean Validation",它提供了一种在Java对象级别上执行验证的方式,通常用于确保输入数据的完整性和准…...
“揭秘网络握手与挥别:TCP三次握手和四次挥手全解析“
前言 在计算机网络中,TCP(传输控制协议)是一种重要的通信协议,用于在网络中的两台计算机之间建立可靠的连接并交换数据。TCP协议通过“三次握手”和“四次挥手”的过程来建立和终止连接,确保数据的准确传输。 一、三…...
Java开发工程师面试题(Spring)
一、Spring Bean的生命周期 生命周期可以分为以下几步: 通过Spring框架的beanFactory工厂利用反射机制创建bean对象。根据set方法或者有参构造方法给bean对象的属性进行依赖注入。判断当前bean对象是否实现相关aware接口,诸如beanNameAware、beanFactor…...
【C++】string类的基础操作
💗个人主页💗 ⭐个人专栏——C学习⭐ 💫点击关注🤩一起学习C语言💯💫 目录 导读 1. 基本概述 2. string类对象的常见构造 3. string类对象的容量操作 4. string类对象的访问及遍历操作 5. 迭代器 6.…...
Java项目:40 springboot月度员工绩效考核管理系统009
作者主页:源码空间codegym 简介:Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 本系统的功能分为管理员和员工两个角色 管理员的功能有: (1)个人中心管理功能,添加管理员账号…...
opengl 学习(三)-----着色器
着色器 分类demo效果解析教程 分类 OPengl C demo #include "glad/glad.h" #include "glfw3.h" #include <iostream> #include <cmath> #include <vector>#include <string> #include <fstream> #include <sstream>…...
电销平台架构的演变与升级
简介 信也科技电销平台承载了公司400多坐席的日常外呼任务,随着公司业务规模不断增长,业务复杂度不断提升,营销模式需要多样化,营销流程需要更加灵活。为了更好地赋能业务、提高客户转化率,电销平台不断升级优化&#…...
轻薄蓝牙工牌室内人员定位应用
在现代化企业管理的背景下,轻薄蓝牙工牌人员定位应用逐渐崭露头角,成为提升企业效率和安全性的重要工具。本文将从轻薄蓝牙工牌的定义、特点、应用场景以及未来发展趋势等方面,对其进行全面深入的探讨。 一、轻薄蓝牙工牌的定义与特点 轻薄…...
好物周刊#46:在线工具箱
https://github.com/cunyu1943 村雨遥的好物周刊,记录每周看到的有价值的信息,主要针对计算机领域,每周五发布。 一、项目 1. twelvet 一款基于 Spring Cloud Alibaba 的权限管理系统,集成市面上流行库,可以作用为快…...
20240306-1-大数据的几个面试题目
面试题目 1. 相同URL 题目: 给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url? 方案1:估计每个文件的大小为50G64320G,远远大于内存限制的4G。所以…...
Vue中如何处理用户权限?
在前端开发中,处理用户权限是非常重要的一个方面。Vue作为一种流行的前端框架,提供了很多便捷的方式来管理用户权限。本文将介绍一些Vue中处理用户权限的方法 1. 使用路由守卫 Vue Router提供了一个功能强大的功能,即导航守卫(N…...
【STM32】HAL库 CubeMX教程---基本定时器 定时
目录 一、基本定时器的作用 二、常用型号的TIM时钟频率 三、CubeMX配置 四、编写执行代码 实验目标: 通过CUbeMXHAL,配置TIM6,1s中断一次,闪烁LED。 一、基本定时器的作用 基本定时器,主要用于实现定时和计数功能…...
2024年最新整理腾讯云学生服务器价格、续费和购买流程
2024年腾讯云学生服务器优惠活动「云校园」,学生服务器优惠价格:轻量应用服务器2核2G学生价30元3个月、58元6个月、112元一年,轻量应用服务器4核8G配置191.1元3个月、352.8元6个月、646.8元一年,CVM云服务器2核4G配置842.4元一年&…...
【QT】重载的信号槽/槽函数做lambda表达式
重载的信号槽 函数指针: int fun(int a,long b) int (*funp)(int, long) fun; 实现回调函数就需要函数指针 信号重载 派生类槽函数发送两个信号 派生类给父类发两个信号 void (SubWidget::*mysigsub)() &SubWidget::sigSub;connect(&subw,mysigsub,t…...
C++之类(一)
1,封装 1.1 封装的引用 封装是C面向对象三大特性之一 封装的意义: 将属性和行为作为一个整体,表现生活中的事物 将属性和行为加以权限控制 1.1.1 封装意义一: 在设计类的时候,属性和行为写在一起,表…...
【工具类】repo是什么,repo常用命令,repo和git和git-repo的关系
1. repo 1. repo 1.1. repo是什么1.2. 安装1.3. repo 命令 1.3.1. repo help1.3.2. repo init1.3.3. repo sync1.3.4. repo upload1.3.5. repo start1.3.6. repo forall 1.4. mainfest 文件1.5. git-repo简介(非android repo)1.6. 参考资料 1.1. repo是什么 Repo 是一个 go…...
Java中可以实现的定时任务策略
Java中可以实现的定时任务策略 文章目录 Java中可以实现的定时任务策略自定义独立线程JDK提供的调度线程池-**ScheduledExecutorService**内核是Spring的Task执行调度quartz调度 #mermaid-svg-mQ9rPqk0Ds3ULnvD {font-family:"trebuchet ms",verdana,arial,sans-seri…...
【目标分类图像增强方法】
图像增强方法及其原理 目标分类图像增强是一种用于提高深度学习模型泛化能力的技术,通过在训练过程中对原始图像进行各种变换来增加模型所见数据的多样性。以下是几种常见的图像增强方法及其原理: 几何变换: 旋转(Rotation&#…...
游戏盾如何应对微商城网站DDoS攻击
游戏盾如何应对微商城网站DDoS攻击?随着电子商务的快速发展,微商城网站已成为众多商家开展在线业务的重要平台。然而,与此同时,网络安全威胁也愈发严重。其中,分布式拒绝服务(DDoS)攻击是一种常…...
安卓手机如何使用JuiceSSH实现公网远程连接本地Linux服务器
文章目录 1. Linux安装cpolar2. 创建公网SSH连接地址3. JuiceSSH公网远程连接4. 固定连接SSH公网地址5. SSH固定地址连接测试 处于内网的虚拟机如何被外网访问呢?如何手机就能访问虚拟机呢? cpolarJuiceSSH 实现手机端远程连接Linux虚拟机(内网穿透,手机端连接Linux虚拟机) …...
RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...
高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...
视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
PAN/FPN
import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...
