当前位置: 首页 > news >正文

Spring Boot - 参数校验:分组校验、自定义注解、嵌套对象全解析

在这里插入图片描述

01 依赖配置

在构建高效的校验体系前,需先完善项目依赖配置。

以下是优化后的依赖示例:

<dependencies><!-- Web 依赖,提供 RESTful 接口支持 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- 校验核心依赖,整合 Hibernate Validator 实现 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>
</dependencies>

注解:

  1. spring-boot-starter-web 依赖引入了 Spring MVC 组件,为构建 Web 应用提供基础支持
  2. spring-boot-starter-validation 依赖整合了 Hibernate Validator 实现,提供 JSR 380 校验规范支持

02 校验注解体系构建

构建完善的校验注解体系是实现数据校验的基础。

以下是企业级校验注解应用示例:

import javax.validation.constraints.*;
import java.time.LocalDate;public class UserRegistrationRequest {// 用户名校验:非空且长度在 4-20 位之间@NotBlank(message = "用户名不能为空")@Size(min = 4, max = 20, message = "用户名长度需在 4-20 位之间")private String username;// 邮箱校验:非空且符合邮箱格式@NotBlank(message = "邮箱不能为空")@Email(message = "邮箱格式不正确")private String email;// 密码校验:非空且长度不少于 8 位@NotBlank(message = "密码不能为空")@Size(min = 8, message = "密码长度至少为 8 位")private String password;// 年龄校验:必须大于 0 且小于 150@Min(value = 1, message = "年龄必须大于 0")@Max(value = 150, message = "年龄不能超过 150")private Integer age;// 出生日期校验:不能晚于当前日期@PastOrPresent(message = "出生日期不能晚于当前日期")private LocalDate birthDate;// 协议同意校验:必须为 true@NotNull(message = "必须同意服务条款")private Boolean termsAccepted;
}

注解:

  1. 使用组合注解实现多维度校验,如 @NotBlank + @Size 实现既校验非空又校验长度的双重校验
  2. 通过明确的错误提示信息,提升系统友好性,便于用户定位问题
  3. 日期类型使用 @PastOrPresent 校验,避免接收未来日期的非法输入

03 控制器校验集成

在控制器层面集成校验逻辑,确保所有入参经过严格校验:

import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;import javax.validation.Valid;@Validated // 启用方法参数校验
@RestController
@RequestMapping("/api/users")
public class UserController {@PostMapping("/register")public ResponseEntity registerUser(@Valid @RequestBody UserRegistrationRequest request) {// 业务逻辑处理return ResponseEntity.ok("用户注册成功");}@GetMapping("/{id}")public ResponseEntity getUserById(@PathVariable Long id) {// 用户查询逻辑return ResponseEntity.ok(new User(id, "示例用户"));}
}

注解:

  1. @Validated 注解启用控制器方法参数校验,配合 @Valid 对请求体进行深度校验

  2. @RequestBody 结合校验注解,实现复杂对象的自动绑定与校验

  3. 通过明确的 HTTP 状态码和响应消息,提供标准化的接口反馈

04 全局异常处理机制

构建完善的全局异常处理机制,统一管理校验异常:

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;import java.util.HashMap;
import java.util.Map;@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(MethodArgumentNotValidException.class)public ResponseEntity handleValidationExceptions(MethodArgumentNotValidException ex) {Map<String, String> errors = new HashMap<>();ex.getBindingResult().getFieldErrors().forEach(error -> {errors.put(error.getField(), error.getDefaultMessage());});return ResponseEntity.badRequest().body(errors);}@ExceptionHandler(Exception.class)public ResponseEntity handleGeneralException(Exception ex) {return ResponseEntity.internalServerError().body("系统内部错误");}
}

注解:

  1. 通过 @RestControllerAdvice 实现全局异常处理,集中管理各类异常
  2. 针对校验异常提取字段级错误信息,返回结构化的错误响应
  3. 提供默认异常处理方法,确保所有未被捕获的异常都能得到妥善处理

05 分组校验实现

在复杂业务场景中,通过分组校验实现条件校验逻辑:

// 定义校验分组接口
public interface CreateGroup {}
public interface UpdateGroup {}// 应用分组校验的 DTO 类
public class UserDTO {@NotBlank(groups = CreateGroup.class, message = "创建时用户名必填")@Size(groups = CreateGroup.class, min = 4, message = "用户名长度至少为 4")private String username;@Email(groups = {CreateGroup.class, UpdateGroup.class}, message = "邮箱格式不正确")private String email;// Getter 和 Setter 方法
}
// 控制器方法应用分组校验
@PostMapping("/users")
public ResponseEntity createUser(@Validated({CreateGroup.class}) UserDTO userDTO) {// 创建用户逻辑return ResponseEntity.ok("用户创建成功");
}@PutMapping("/users/{id}")
public ResponseEntity updateUser(@Validated({UpdateGroup.class}) UserDTO userDTO) {// 更新用户逻辑return ResponseEntity.ok("用户更新成功");
}

注解:

  1. 通过定义接口实现校验分组,灵活控制不同场景下的校验规则

  2. 在 DTO 类中指定各字段所属的校验分组,实现条件校验

  3. 控制器方法通过指定校验分组,精确控制校验逻辑的触发场景

06 嵌套对象校验

处理复杂对象嵌套场景下的校验需求:

// 嵌套对象校验示例
public class Order {@NotBlank(message = "订单号不能为空")private String orderId;@DecimalMin(value = "0.01", message = "订单金额必须大于 0.01")private BigDecimal amount;// 嵌套 Customer 对象,启用深度校验@Validprivate Customer customer;
}public class Customer {@NotBlank(message = "客户名称不能为空")private String name;@Email(message = "邮箱格式不正确")private String email;// 嵌套 Address 对象,继续深度校验@Validprivate Address address;
}public class Address {@NotBlank(message = "地址不能为空")private String street;@NotBlank(message = "城市不能为空")private String city;
}
// 控制器方法应用嵌套校验
@PostMapping("/orders")
public ResponseEntity createOrder(@Valid @RequestBody Order order) {// 订单创建逻辑return ResponseEntity.ok("订单创建成功");
}

注解:

  1. 使用 @Valid 注解启用嵌套对象的深度校验,支持多层对象嵌套
  2. 校验规则沿用对象关系,自动继承父对象的校验上下文
  3. 全局异常处理器自动捕获嵌套校验错误,返回详细的错误路径信息

07 自定义校验注解开发

针对特殊业务场景开发自定义校验注解:

// 自定义注解定义
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneValidator.class)
public @interface ValidPhoneNumber {String message() default "手机号格式不正确";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};
}// 自定义校验实现
public class PhoneValidator implements ConstraintValidator<ValidPhoneNumber, String> {private static final String PHONE_REGEX = "^1[3-9]\\d{9}$";@Overridepublic void initialize(ValidPhoneNumber constraintAnnotation) {// 初始化逻辑}@Overridepublic boolean isValid(String value, ConstraintValidatorContext context) {if (value == null || value.trim().isEmpty()) {return true; // 空值不校验}return Pattern.matches(PHONE_REGEX, value);}
}
// 自定义注解应用示例
public class ContactInfo {@ValidPhoneNumberprivate String phone;// Getter 和 Setter 方法
}

注解:

  1. 通过 @Constraint 注解指定校验器实现类,完成自定义校验注解开发
  2. 校验器实现类需继承 ConstraintValidator,重写 isValid 方法定义校验逻辑
  3. 自定义注解可应用于字段或参数,与内置注解使用方式一致
http://www.lqws.cn/news/595567.html

相关文章:

  • 【CVPR2024】计算机视觉|EGTR : Transformer中挖掘关系图,场景图生成SOTA!
  • 141.在 Vue 3 中使用 OpenLayers Link 交互:把地图中心点 / 缩放级别 / 旋转角度实时写进 URL,并同步解析显示
  • 利用springEvent,进行服务内部领域事件处理
  • 使用 icinga2 写入 TDengine
  • 用lines_gauss的width属性提取缺陷
  • C++ 11 中 condition_variable 的探索与实践
  • python 调用C/C++动态库
  • Apache HTTP Server部署全攻略
  • 用于快速训练收敛的 Conditional DETR
  • 基于.Net的Web API 控制器及方法相关注解属性
  • 数字ic后端设计从入门到精通10(含fusion compiler, tcl教学)静态时序分析
  • 3D 商品展示与 AR 试戴能为珠宝行业带来一些便利?
  • 什么是测试用例?它的核心要素有哪些?
  • docker desktop部署本地gitlab服务
  • 【仿muduo库实现并发服务器】Connection模块
  • 【仿muduo库实现并发服务器】Acceptor模块
  • 笔记/计算机网络
  • ChatGPT使用限额记录与插件统计
  • Softhub软件下载站实战开发(九):编写软件配置管理界面
  • Electron 应用打包与分发:从开发到交付的完整指南
  • Call、Apply、Bind详解
  • 如何进行Edge版本回退及禁用更新
  • 结构光相机:重塑工业自动化的“智慧之眼”,驱动智能制造新未来
  • 深度剖析:基于AOP、自定义注解与设计模式构建高度可定制的分布式锁解决方案
  • 亚马逊云科技中国峰会:数新智能CTO原攀峰详解一站式AI原生数智平台DataCyber在Amazon EKS的实践
  • 基于SSM万华城市货运服务系统的设计与实现
  • eNSP实验一:IPv4编址及IPv4路由基础
  • 新手向:从零开始Node.js超详细安装、配置与使用指南
  • 业务系统-AI 智能导航设计(系统设计篇 下)
  • 制作一款打飞机游戏74:游戏原型