ParamValidator使用
@Valid与 ParamValidator.validate
1. @Valid
的特点
-
使用方式:
-
@Valid
是 Java Bean Validation 的注解(通常结合 Hibernate Validator 使用),用于自动验证方法参数或字段的约束。 -
配合 JSR-303/JSR-380(例如
@NotNull
,@Size
,@Pattern
等)一起工作。
-
-
工作原理:
-
在控制器层(例如 Spring MVC 的方法参数)使用时,Spring 会自动调用 Validator 检查参数是否符合约束。
-
如果校验失败,会抛出
ConstraintViolationException
或MethodArgumentNotValidException
。
-
-
适用场景:
-
控制器层请求参数的自动校验,减少手动调用校验逻辑。
-
结合注解,简化代码逻辑。
-
2. ParamValidator.validate
的特点
-
使用方式:
-
ParamValidator.validate
是一种自定义方法校验工具,通常通过调用静态方法对对象进行校验,支持动态加载验证规则类或分组校验。 -
比如:
ParamValidator.validate(data, AddGroup.class, UpdateGroup.class);
-
-
工作原理:
-
手动触发验证逻辑。
-
根据传入的规则类、分组类等动态验证参数。
-
-
适用场景:
-
灵活适配复杂的验证逻辑。
-
控制器以外的场景(例如服务层或工具类)。
-
3. 是否重复校验的分析
-
两者结合使用: 如果在控制器层同时使用
@Valid
和ParamValidator.validate
,可能会导致重复校验。例如:java@PostMapping("/submit") public ResponseEntity submit(@Valid @RequestBody MyRequest request) { ParamValidator.validate(request, AddGroup.class); return ResponseEntity.ok(); }
上述代码中,
@Valid
已经自动对参数进行校验,而ParamValidator.validate
可能重复执行类似的校验。 -
解决方案:
-
避免重复校验:
-
使用
@Valid
处理简单的静态校验场景。 -
使用
ParamValidator.validate
处理动态或复杂的业务校验逻辑。
-
-
明确职责划分:
-
控制器层使用
@Valid
进行输入参数的初步校验。 -
服务层或工具类使用
ParamValidator.validate
进行业务逻辑的深度校验。
-
-
-
推荐设计:
-
如果
ParamValidator.validate
的规则与@Valid
重合,可以仅选择其一,避免性能和代码冗余问题。
-
ParamValidator中group使用
1. ParamValidator 和 group 的使用
定义分组类
分组类通常是一个空的接口,表示不同的校验场景,例如新增和更新操作。
示例代码:定义分组类
public interface AddGroup {} // 新增分组
public interface UpdateGroup {} // 更新分组
字段校验规则与分组绑定
通过 Bean Validation 的注解(如 @NotNull
、@Size
等),结合 groups
属性,将字段约束绑定到具体分组。
示例代码:实体类校验规则
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public class UserRequest {
@NotNull(groups = AddGroup.class, message = "ID不能为空(新增场景)")
private String id;
@Size(groups = UpdateGroup.class, min = 5, max = 50, message = "名称长度必须在5到50之间(更新场景)")
private String name;
// Getters and Setters
}
-
@NotNull(groups = AddGroup.class)
:仅在新增操作时验证id
字段。 -
@Size(groups = UpdateGroup.class)
:仅在更新操作时验证name
字段。
调用 ParamValidator.validate
在业务逻辑中,通过 ParamValidator.validate
指定分组,动态校验不同场景的数据。
示例代码:动态调用分组校验
public void processRequest(UserRequest request, String operationType) {
if ("ADD".equals(operationType)) {
ParamValidator.validate(request, AddGroup.class); // 新增校验
} else if ("UPDATE".equals(operationType)) {
ParamValidator.validate(request, UpdateGroup.class); // 更新校验
}
}
2. 注解 @Valid 的结合使用
在控制器层启用 @Valid
当参数使用 @Valid
注解时,可以结合 Spring MVC 的分组校验功能,在自动校验过程中指定校验分组。
示例代码:结合 @Valid 和分组
@PostMapping("/submit")
public ResponseEntity submit(@Validated(AddGroup.class) @RequestBody UserRequest request) {
// 如果校验失败,Spring 会自动抛出异常
return ResponseEntity.ok("Success");
}
-
@Validated(AddGroup.class)
:指定校验分组为AddGroup
。 -
Spring 自动调用 Bean Validation,校验请求参数是否符合约束条件。
与 ParamValidator.validate 联合使用
在某些场景下,控制器层可以使用 @Valid
做初步校验,服务层使用 ParamValidator.validate
进一步校验复杂规则。
示例代码:结合使用
@PostMapping("/submit")
public ResponseEntity submit(@Validated(AddGroup.class) @RequestBody UserRequest request) {
// 控制器层校验基础规则
ParamValidator.validate(request, AddGroup.class); // 服务层校验逻辑
return ResponseEntity.ok("Success");
}
-
控制器层使用
@Valid
处理简单规则。 -
服务层使用
ParamValidator.validate
实现动态分组和复杂逻辑。
1. 多组的定义
可以通过 groups
属性为字段同时指定多个分组。分组的定义仍然是空接口,表示不同的校验场景。
示例:定义分组接口
public interface AddGroup {} // 新增分组
public interface UpdateGroup {} // 更新分组
public interface DeleteGroup {} // 删除分组
2. 为字段指定多个分组
在字段注解的 groups
属性中,传入分组的类数组 Class<?>[]
,这样字段可以在多个分组中应用不同的校验规则。
示例代码:一个字段多组
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public class UserRequest {
@NotNull(groups = {AddGroup.class, UpdateGroup.class}, message = "ID不能为空(新增和更新)")
private String id;
@Size(groups = {UpdateGroup.class, DeleteGroup.class}, min = 5, max = 50,
message = "名称长度必须在5到50之间(更新和删除)")
private String name;
// Getters and Setters
}
解释:
-
id
字段:-
同时在
AddGroup
和UpdateGroup
中校验@NotNull
。 -
当校验使用这两个分组时,都会验证
id
是否为空。
-
-
name
字段:-
在
UpdateGroup
和DeleteGroup
中校验长度范围。
-
3. 使用多组进行校验
当使用多个分组时,可以灵活选择分组,调用 Validator
或 @Validated
进行校验。
(1)使用 Validator
动态校验
通过 Java 的 Validator 手动校验,并指定分组:
示例代码:动态校验多个分组
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.ConstraintViolation;
import java.util.Set;
public class ValidatorExample {
public static void main(String[] args) {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
UserRequest request = new UserRequest();
request.setId(null); // 演示校验失败
request.setName("abc");
// 针对 AddGroup 和 UpdateGroup 同时校验
Set<ConstraintViolation<UserRequest>> violations = validator.validate(request, AddGroup.class, UpdateGroup.class);
for (ConstraintViolation<UserRequest> violation : violations) {
System.out.println(violation.getMessage());
}
}
}
输出示例:
ID不能为空(新增和更新)
名称长度必须在5到50之间(更新和删除)
(2)结合 Spring 的 @Validated
在 Spring 控制器中,可以使用 @Validated
注解指定多个分组。
示例代码:Spring 中多组校验
@PostMapping("/submit")
public ResponseEntity submit(@Validated({AddGroup.class, UpdateGroup.class}) @RequestBody UserRequest request) {
return ResponseEntity.ok("校验通过!");
}
4. 注意事项
-
校验规则的覆盖:
-
如果一个字段同时存在于多个分组,并且有重复的校验注解,那么所有注解规则都会生效。
-
不同分组中相同字段的错误信息可能会叠加。
-
-
校验分组的优先级:
-
多个分组按声明的顺序逐一校验,后续分组的校验不会覆盖前面的结果。
-
总结
-
定义多个分组:通过接口定义不同的校验场景(例如新增、更新、删除)。
-
一个字段多组校验:在字段注解中通过
groups
属性指定多个分组。 -
灵活调用:结合 Java 原生 Validator 或 Spring 的
@Validated
,动态指定分组进行校验。 - 每个分组都会独立地进行校验
@Valid与@Validated
@Valid
和 @Validated
都用于 Java Bean Validation 框架中的数据校验,但它们的用途和工作方式有所不同。下面是它们的详细对比:
1. @Valid
-
来源:
-
@Valid
是 Java 的标准注解,属于 JSR-303/JSR-380(Bean Validation)的规范。 -
它需要配合校验框架(如 Hibernate Validator)使用。
-
-
作用:
-
用于方法参数或对象字段的校验。
-
当校验失败时,会抛出
ConstraintViolationException
(通常是自动处理的)。
-
-
特点:
-
嵌套对象校验:支持对嵌套的对象进行校验。例如,校验
List
或子对象中的字段。 -
无法指定分组:
@Valid
不支持分组校验,仅适用于全局验证规则。
-
-
典型用法:
javapublic class UserRequest { @NotNull(message = "用户名不能为空") private String username; @Valid // 嵌套对象校验 private Address address; // Getters and Setters } @PostMapping("/submit") public ResponseEntity submit(@Valid @RequestBody UserRequest request) { // 自动校验请求参数 return ResponseEntity.ok("校验通过"); }
2. @Validated
-
来源:
-
@Validated
是 Spring Framework 提供的注解。 -
它扩展了 Bean Validation,并支持分组校验。
-
-
作用:
-
用于启用 Bean Validation 的功能,同时允许指定校验分组。
-
常用于控制器层的方法参数校验,也可以应用于类级别。
-
-
特点:
-
支持分组:可以通过分组校验不同场景下的数据。例如新增和更新场景需要不同的规则。
-
适用于 Spring:通常结合 Spring 的校验机制使用。
-
-
典型用法:
javapublic class UserRequest { @NotNull(groups = AddGroup.class, message = "ID不能为空") private String id; @Size(groups = UpdateGroup.class, min = 5, max = 50, message = "名称长度必须在5到50之间") private String name; // Getters and Setters } @PostMapping("/submit") public ResponseEntity submit(@Validated(AddGroup.class) @RequestBody UserRequest request) { // 启用分组校验,校验 AddGroup 中的规则 return ResponseEntity.ok("校验通过"); }
3. 对比总结
特性 | @Valid | @Validated |
---|---|---|
来源 | JSR-303/Bean Validation | Spring Framework |
分组校验支持 | 不支持 | 支持(通过指定分组类实现) |
嵌套对象校验 | 支持 | 支持 |
应用场景 | 标准的 Java 校验框架支持 | Spring 项目的扩展校验功能 |
典型用法 | 简单校验(无分组、嵌套校验) | 复杂校验(分组和场景区分) |
4. 注意事项
-
嵌套校验:
-
如果需要校验嵌套对象的字段,必须使用
@Valid
。@Validated
不能单独完成嵌套校验。
-
-
分组校验:
-
@Valid
不能指定分组,如果需要分组校验,必须使用@Validated
。
-
-
异常处理:
-
Spring 自动处理校验失败异常(如
MethodArgumentNotValidException
),可以通过@ControllerAdvice
自定义异常处理逻辑。
-
两者可以结合使用的代码示例
1. 实体类定义
定义两个嵌套的类,并为字段设置分组和校验规则:
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.validation.Valid;
public class Address {
@NotNull(message = "城市不能为空")
private String city;
@Size(min = 2, max = 20, message = "街道名称长度必须在2到20之间")
private String street;
// Getters and Setters
}
public class UserRequest {
@NotNull(groups = AddGroup.class, message = "用户ID不能为空(新增场景)")
private String userId;
@Size(groups = UpdateGroup.class, min = 5, max = 50, message = "用户名长度必须在5到50之间(更新场景)")
private String username;
@Valid // 启用嵌套对象校验
private Address address;
// Getters and Setters
}
解释:
-
嵌套对象:
-
@Valid
应用于address
字段,使得UserRequest
的嵌套对象Address
也会被校验。 -
即使
@Validated
仅用于分组校验,嵌套对象校验依然由@Valid
触发。
-
-
分组校验:
-
userId
绑定到AddGroup
分组,校验新增场景。 -
username
绑定到UpdateGroup
分组,校验更新场景。
-
2. 分组接口定义
定义校验分组接口:
public interface AddGroup {} // 新增场景
public interface UpdateGroup {} // 更新场景
3. 控制器中结合使用
在控制器中使用 @Validated
和 @Valid
实现分组校验和嵌套对象校验:
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/users")
public class UserController {
@PostMapping("/add")
public ResponseEntity addUser(@Validated(AddGroup.class) @RequestBody UserRequest request) {
// 自动校验新增场景的规则,包括嵌套的 Address 对象
return ResponseEntity.ok("用户新增成功!");
}
@PostMapping("/update")
public ResponseEntity updateUser(@Validated(UpdateGroup.class) @RequestBody UserRequest request) {
// 自动校验更新场景的规则,包括嵌套的 Address 对象
return ResponseEntity.ok("用户更新成功!");
}
}
解释:
-
分组校验:
-
@Validated(AddGroup.class)
会触发AddGroup
分组的规则。 -
@Validated(UpdateGroup.class)
会触发UpdateGroup
分组的规则。
-
-
嵌套校验:
-
无论使用哪个分组,嵌套对象
Address
的校验规则仍然由@Valid
自动触发。
-
4. 测试数据示例
-
新增用户请求(新增场景校验触发
AddGroup
):json{ "userId": null, // 错误: 用户ID不能为空 "username": "test", // 不校验用户名(非 AddGroup 规则) "address": { "city": "Shanghai", // 正确 "street": "" // 错误: 街道名称长度必须在2到20之间 } }
-
更新用户请求(更新场景校验触发
UpdateGroup
):json{ "userId": null, // 不校验用户ID(非 UpdateGroup 规则) "username": "t", // 错误: 用户名长度必须在5到50之间 "address": { "city": "", // 错误: 城市不能为空 "street": "Main Street" // 正确 } }
5. 注意事项
-
嵌套校验依赖
@Valid
:-
只能通过
@Valid
触发嵌套对象的校验,即使分组校验通过,嵌套字段仍需独立处理。
-
-
分组与嵌套校验独立:
-
分组校验和嵌套校验互不冲突,分组校验只影响直接作用于实体类的字段。
-
-
结合异常处理:
-
在控制器中添加全局异常处理,返回校验失败的信息:
java@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity handleValidationException(MethodArgumentNotValidException ex) { return ResponseEntity.badRequest().body(ex.getBindingResult().getAllErrors()); } }
-
总结
通过结合使用 @Valid
和 @Validated
,可以实现:
-
嵌套对象的校验(通过
@Valid
)。 -
基于分组的动态校验(通过
@Validated
)。
如果city也需要分组 可以实现吗?
1. 修改实体类
在 Address
类中,为 city
字段添加分组校验规则。
示例代码:分组校验扩展
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public class Address {
@NotNull(groups = {AddGroup.class, UpdateGroup.class}, message = "城市不能为空(新增和更新场景)")
private String city;
@Size(groups = DeleteGroup.class, min = 2, max = 20, message = "街道名称长度必须在2到20之间(删除场景)")
private String street;
// Getters and Setters
}
解释:
-
city
字段:-
绑定到
AddGroup
和UpdateGroup
分组校验,新增和更新场景都会检查该字段是否为空。
-
-
street
字段:-
绑定到
DeleteGroup
分组校验,删除场景校验街道名称长度。
-
2. 修改主实体类
在 UserRequest
中,仍然保持对嵌套对象的 @Valid
校验。
示例代码:嵌套分组校验
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public class UserRequest {
@NotNull(groups = AddGroup.class, message = "用户ID不能为空(新增场景)")
private String userId;
@Size(groups = UpdateGroup.class, min = 5, max = 50, message = "用户名长度必须在5到50之间(更新场景)")
private String username;
@Valid
private Address address; // 嵌套对象校验,支持分组逻辑
// Getters and Setters
}
嵌套字段:
-
当使用分组校验(如
AddGroup
和UpdateGroup
),嵌套的Address
对象会根据分组规则验证其字段。
3. 控制器实现
使用 @Validated
指定分组,并触发对 city
的分组校验。
示例代码:控制器分组校验
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/users")
public class UserController {
@PostMapping("/add")
public ResponseEntity addUser(@Validated(AddGroup.class) @RequestBody UserRequest request) {
// 校验新增场景的规则,包括嵌套对象 Address 的分组规则
return ResponseEntity.ok("新增用户成功!");
}
@PostMapping("/update")
public ResponseEntity updateUser(@Validated(UpdateGroup.class) @RequestBody UserRequest request) {
// 校验更新场景的规则,包括嵌套对象 Address 的分组规则
return ResponseEntity.ok("更新用户成功!");
}
}
4. 测试请求示例
新增场景(触发 AddGroup
)
{
"userId": null, // 错误: 用户ID不能为空
"username": "test", // 不校验用户名(非 AddGroup 规则)
"address": {
"city": null, // 错误: 城市不能为空
"street": "Main Street" // 不校验街道(非 AddGroup 规则)
}
}
更新场景(触发 UpdateGroup
)
{
"userId": null, // 不校验用户ID(非 UpdateGroup 规则)
"username": "t", // 错误: 用户名长度必须在5到50之间
"address": {
"city": "", // 错误: 城市不能为空
"street": "Main Street" // 不校验街道(非 UpdateGroup 规则)
}
}
删除场景(触发 DeleteGroup
)
{
"address": {
"city": "Shanghai", // 不校验城市(非 DeleteGroup 规则)
"street": "St" // 错误: 街道名称长度必须在2到20之间
}
}
5. 注意事项
-
嵌套对象分组校验:
-
嵌套对象需要
@Valid
注解才能触发校验,且会基于父对象指定的分组逻辑。
-
-
分组的独立性:
-
每个分组会分别校验其绑定的规则,但不同字段的规则不会互相干扰。
-
-
性能优化:
-
如果字段规则完全相同,可以考虑合并分组逻辑以避免重复校验。
-
- 点赞
- 收藏
- 关注作者
评论(0)