springboot全局异常实现以及@Valid和@Validated优雅实现入参验证

文章介绍了SpringBoot项目中全局异常处理的重要性,包括减少代码冗余、提供友好的错误信息和异常日志记录。通过自定义异常类和全局异常处理器,可以统一处理不同类型的异常,并提供了具体的异常捕获配置类示例。此外,文章还提到了@Validated和@Valid在数据验证中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前序

为什么要有全局异常❓

  1. 统一异常处理:在开发过程中,可能会遇到多个地方抛出的不同类型的异常,如果没有统一的异常处理机制,就需要在每个可能发生异常的地方进行单独处理。这样会导致代码冗余,增加开发和维护的工作量。全局异常处理机制通过统一捕获和处理异常,避免了在各个地方重复编写相同的异常处理逻辑,提高了代码的可重用性和可维护性。
  2. 错误信息的友好展示:默认情况下,当发生异常时,Spring Boot会返回一些默认的错误信息,例如堆栈跟踪信息。这些信息对于用户来说可能不够友好,也不利于问题的定位和解决。通过全局异常处理机制,我们可以自定义异常的返回消息、状态码等,使错误信息更加友好和可读。这样用户在遇到异常时能够更好地理解问题,并根据错误信息采取相应的操作。
  3. 异常日志的记录和追踪:在应用程序中,异常日志的记录对于排查问题和定位Bug非常重要。全局异常处理机制可以在异常发生时记录异常日志,包括异常类型、异常信息、发生异常的位置等。通过记录异常日志,我们可以更方便地追踪异常的来源,并及时发现和解决潜在的问题。
  4. 统一错误处理逻辑:在应用程序中,可能存在一些通用的错误处理逻辑,例如处理未授权异常、处理业务异常等。通过全局异常处理机制,我们可以集中管理这些通用的错误处理逻辑,并根据不同的异常类型提供相应的处理方式。这样可以使应用程序的错误处理更加一致,减少了重复的代码和逻辑,提高了代码的可读性和可维护性。

什么是全局异常处理❓

全局异常处理是指捕获应用程序中的所有异常并提供

<think>我们正在讨论SpringBoot中使用@Validated注解对List<对象>类型的数进行校验的问题。根据提供的引用,我们可以总结出:-引用[1]说明了SpringBoot使用@Validated需要引spring-boot-starter-validation依赖。-引用[2]展示了如何在一个对象中嵌另一个对象进行校验(使用@Valid)。-引用[3]展示了在Controller方法数上使用@Validated注解,并且指定分组校验。用户的问题:如何对List<对象>类型的数进行校验?我们知道,在SpringBoot中,对于单个对象的校验,我们直接在对象前加上@Valid@Validated即可。但是,对于List<对象>,我们需要额外的处理。解决方案:1.使用一个包装类,将List<对象>作为该包装类的一个属性,然后对这个属性标注@Valid(或@Validated)注解。这样,Spring在绑定数据时会对List中的每个对象进行校验。2.使用自定义的校验注解校验器。这里我们采用第一种方式,因为它更符合SpringBoot的校验机制,并且与引用[2]中嵌校验的方式一致。步骤:①创建包装类,将List<对象>作为属性,并在该属性上添加@Valid注解(注意:不能直接使用@Validated在这里,@Validated是用在类方法上的,而@Valid是用在字段、方法数或方法返回值的)。②在Controller方法的数上使用@Validated注解(如果使用分组校验则需要,否则可以直接用@Valid)来触发校验。注意:SpringBoot在2.3版本之后,需要显式添加依赖spring-boot-starter-validation(如引用[1]所示)。下面通过一个例子说明:假设我们有一个StudentDTO类,其中有一些校验注解:```javapublicclassStudentDTO{@NotBlank(message="学生姓名不能为空")privateStringname;@Min(value=18,message="年龄必须大于18岁")privateintage;//省略gettersetter}```然后,我们创建一个包装类,用于封装List<StudentDTO>:```javapublicclassStudentListDTO{@ValidprivateList<StudentDTO>students;//必须提供settergetterpublicList<StudentDTO>getStudents(){returnstudents;}publicvoidsetStudents(List<StudentDTO>students){this.students=students;}}```最后,在Controller中,我们接收这个包装类,并在数前加上@Valid@Validated(注意:这里不能直接使用@Validated来校验包装类内部的List,但可以在数上使用@Validated来触发校验,不过更常见的做法是使用@Valid):```java@RestController@RequestMapping("/students")publicclassStudentController{@PostMapping("/batch")publicResultaddBatch(@RequestBody@ValidStudentListDTOstudentList){//处理批量新增学生returnResult.ok();}}```这样,当请求中的List<StudentDTO>被传时,Spring会为StudentListDTO中的students字段(因为标注了@Valid)进行校验,从而校验List中每一个StudentDTO对象。但是,如果不想使用包装类,可以直接在Controller方法的数上使用@Validated(在类上)配合@Valid在List<StudentDTO>数前。然而,根据Spring的文档,对于直接是List类型的数,我们需要在数前同时使用@Valid@Validated(在类上)?实际上,SpringBoot默认不支持直接对List<对象>进行校验,因为它是一个集合,而校验注解不能直接写在集合类型上。所以,使用包装类是最直接的方法。另一种方案:在Controller方法上使用@Validated(类级别),然后在数前使用@Valid(针对包装类)或者使用@Valid注解在List上?然而,当我们尝试在List数前直接使用@Valid时,Spring并不会递归校验其中的对象(除非我们使用@Valid在包装类的字段上)。总结:为了对List<对象>进行校验,我们需要将List放一个包装类中,并对该字段使用@Valid注解,然后在Controller方法的数上使用@Valid@Validated(分组校验时)。另外,引用[3]提到了分组校验。如果我们需要在List中的对象上使用分组校验,那么可以在@Valid(在包装类字段上)后无法指定分组,因此需要改为在包装类字段上使用@Validated(分组.class)?但注意,@Valid不支持分组,而@Validated支持分组。所以我们可以这样调整:修改包装类:```javapublicclassStudentListDTO{@Validated(Insert.class)//但是,@Validated不能用在字段上,只能用在类型方法上?所以这里不行!privateList<StudentDTO>students;//...}```这样写是错误的,因为@Validated注解不能用在字段上。因此,如果我们想对List中的对象进行分组校验,我们可以将@Validated注解放在Controller类上,然后在数前使用@Validated(分组.class)?但是,这样会作用于整个类,而且数上的@Validated只能用于数上(它也可以指定分组)。然而,在数上使用@Validated(分组.class)配合包装类字段上的@Valid是不行的,因为分组信息不会传递下去。正确的做法是在包装类的字段上使用@Valid(不支持分组),然后在StudentDTO的校验注解上指定分组(这样如果没有激活该分组,则不会校验)。但如果我们激活了分组,那么在字段上使用@Valid会触发所有未指定分组的约束以及指定了当前激活分组的约束。因此,我们可以:在Controller方法数上使用@Validated(分组.class),并且数是包装类(包装类的字段上使用@Valid):```java@RestController@RequestMapping("/students")@Validated//如果整个Controller需要分组校验,可以在类上添加@Validated,但这里我们不需要,因为我们要在方法数上指定分组publicclassStudentController{@PostMapping("/batch")publicResultaddBatch(@RequestBody@Validated(Insert.class)StudentListDTOstudentList){//处理批量新增学生returnResult.ok();}}```然后,在StudentListDTO中:```javapublicclassStudentListDTO{@Valid//这里会激活StudentDTO中所有默认的约束(没有分组的)与当前激活分组(Insert)匹配的约束privateList<StudentDTO>students;//...}```在StudentDTO中,我们需要在约束注解上指定分组(例如Insert):```javapublicclassStudentDTO{@NotBlank(message="学生姓名不能为空",groups=Insert.class)//指定Insert分组privateStringname;@Min(value=18,message="年龄必须大于18岁",groups=Insert.class)//指定Insert分组privateintage;//省略gettersetter}```注意:如果约束注解上指定了分组(例如Insert.class),那么当使用@Valid进行校验时,需要校验器被激活相应的分组(例如在Controller方法的数上使用@Validated(Insert.class))。在这种情况下,包装类字段上的@Valid会传递这个分组吗?实际上,分组校验不会通过@Valid传递。在Spring的校验中,在方法数上使用的@Validated(Insert.class)会激活整个对象图的Insert分组校验。也就是说,它会递归校验所有满足Insert分组的约束。因此,在包装类字段上的@Valid虽然没有指定分组,但会被传递当前激活的分组(即Insert分组)。所以,这种方式是可行的。结论:在SpringBoot中,对List<对象>进行校验的可行方法是将其包装在一个包装类中,然后在包装类的该字段上添加@Valid注解(不需要分组)或@Validated注解(但@Validated不能用在字段上,所以用@Valid),然后在Controller方法的数上使用@Validated并指定分组(如果需要分组校验)。这样,Spring就会递归校验List中的每个对象。另外,如果不想使用包装类,可以使用自定义校验注解配合校验器,但这需要编写更多代码。因此,使用包装类是最简便的方法。生成相关问题:1.如何在分组校验中使用List<对象>?2.如果不使用包装类,如何实现对List<对象>的校验?3.在SpringBoot中,@Valid@Validated有什么区别?4.分组校验的继承关系如何处理?注意:在以上方案中,我们使用的是SpringBoot默认的校验框架(HibernateValidator),所以需要确保引了正确的依赖。下面,我们按照用户的要求总结回答:在SpringBoot中,使用@Validated注解对List<对象>类型的数进行校验,不能直接在List<对象>数前使用@Validated@Valid。但是,我们可以通过将List<对象>封装在一个包装类中,然后对包装类中代表List的字段添加@Valid注解(也可以结合分组校验)。然后在Controller方法数上使用@Validated(需要分组时指定分组)或者@Valid(无分组)来触发校验。示例代码:依赖(如引用[1]):```xml<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId><version>2.7.4</version></dependency>```包装类:```javapublicclassStudentListDTO{@Valid//这里触发List中每个对象的校验privateList<StudentDTO>students;//getterandsetter}```控制器:```java@RestControllerpublicclassStudentController{@PostMapping("/batchAdd")publicStringbatchAddStudents(@RequestBody@ValidStudentListDTOstudentListDTO){//业务处理return"success";}}```如果需要分组校验,则:```java@PostMapping("/batchAdd")publicStringbatchAddStudents(@RequestBody@Validated(Insert.class)StudentListDTOstudentListDTO){//业务处理return"success";}```同时,在StudentDTO类的字段上指定分组(例如Insert.class)。这样,SpringBoot就会对List中的每个对象进行校验。注意:如果List中的某个对象校验失败,会抛出MethodArgumentNotValidException异常,我们可以通过全局异常处理器捕获并处理。可行性:完全可行。</think> 在 Spring Boot 中使用 `@Validated` 注解对 `List<对象>` 类型的数进行校验是**完全可行的**,但需要结合 `@Valid` 注解实现。以下是具体方法实现步骤: --- ### ✅ 使用方法(需结合 `@Valid` 注解) #### 步骤 1:创建包裹 List 的包装类 ```java public class StudentListRequest { @Valid // 关键注解:触发 List 内对象的校验规则 private List<StudentDTO> students; // 必须添加 getter/setter public List<StudentDTO> getStudents() { return students; } public void setStudents(List<StudentDTO> students) { this.students = students; } } ``` #### 步骤 2:在 DTO 对象中定义校验规则 ```java public class StudentDTO { @NotBlank(message = "姓名不能为空") private String name; @Min(value = 18, message = "年龄必须≥18岁") private Integer age; // getter/setter } ``` #### 步骤 3:在 Controller 中使用 `@Validated` ```java @PostMapping("/batch/add") public ResponseEntity<?> batchAddStudents( @RequestBody @Validated StudentListRequest request // 校验包裹类 ) { // 业务逻辑 return ResponseEntity.ok("校验成功"); } ``` --- ### ⚡ 校验触发流程 ```mermaid graph TD A[HTTP 请求] --> B[JSON 解析为 StudentListRequest] B --> C{Spring 校验流程} C -->|扫描包裹类| D[@Valid 触发校验] D --> E[遍历校验 List<StudentDTO>] E --> F[应用 StudentDTO 的 @NotBlank/@Min 等规则] F --> G[校验失败抛出 MethodArgumentNotValidException] ``` --- ### 🔍 关键点说明 1. **`@Valid` 与 `@Validated` 分工**: - `@Valid`(JSR-303 标准):用于**字段级**递归校验(必须加在 List 字段上) - `@Validated`(Spring 扩展):用于**类/方法数级**校验(加在 Controller 数上) 2. **校验规则继承**: - List 中的对象(如 `StudentDTO`)支持所有标准校验注解: ```java @Email, @Size, @Pattern, @NotNull // 等 ``` - 支持**分组校验**(如 `@Validated(Insert.class)`),需在包裹类数指定分组 3. **错误处理**: - 校验失败自动返回 400 状态码 - 错误信息结构示例: ```json { "timestamp": "2023-05-01T12:00:00", "status": 400, "error": "Bad Request", "errors": [ { "field": "students[0].name", "message": "姓名不能为空" }, { "field": "students[2].age", "message": "年龄必须≥18岁" } ] } ``` --- ### ⚠️ 注意事项 1. **不支持直接校验裸 List 数**: ```java // 错误写法:不会触发校验! @PostMapping("/error-example") public void errorExample(@RequestBody @Validated List<StudentDTO> list) {} ``` 2. **必须引依赖**: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> ``` 3. **嵌套校验**: - 如需校验多层嵌套对象(如 `StudentDTO` 中包含 `AddressDTO`),需在嵌套对象字段也添加 `@Valid` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只牛博

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值