点击上方“程序员蜗牛g”,选择“设为星标”
跟蜗牛哥一起,每天进步一点点
程序员蜗牛g
大厂程序员一枚 跟蜗牛一起 每天进步一点点
33篇原创内容
**
公众号
通过简单配置即可自动校验数据,同时还提供了一套完善且灵活的编程式验证接口,允许开发者根据实际需求手动编写验证逻辑,实现更复杂、更精细化的数据校验。
如下编程式验证的简单示例:
public class UserValidator implements Validator { @Override public boolean supports(Class<?> clazz) { return User.class.isAssignableFrom(clazz) ; } @Override public void validate(Object target, Errors errors) { User user = (User) target; // 自定义验证逻辑 }}
接下来,我们以案例的方式详细介绍基于编程式的验证。
2.实战案例
2.1 API介绍
以下是创建自定义验证器对象的一般步骤:
- 创建一个类,实现org.springframework.validation.Validator接口
- 重写supports()方法,以指定此验证器支持验证的类。
- 实现validate()或validateObject()方法,以定义实际的验证逻辑。
- 使用ValidationUtils.rejectIfEmpty()等工具方法,以给定的错误代码拒绝给定字段。
- 我们也可以直接调用Errors.rejectValue()方法,以添加其他类型的错误。
如下示例:
import org.springframework.validation.Errors;import org.springframework.validation.ValidationUtils;import org.springframework.validation.Validator;
@Componentpublic class UserValidator implements Validator { @Override public boolean supports(Class<?> clazz) { return User.class.isAssignableFrom(clazz); } @Override public void validate(Object target, Errors errors) { User user = (User) target; // 校验username字段 ValidationUtils.rejectIfEmptyOrWhitespace(errors, "username", "field.required", "Username must not be empty."); // 你也可以自定义自己的验证逻辑 }}
接下来,如何使用呢?我们可以new每次创建实例,也可以通过注入的方式直接使用,如下示例:
public class UserService { private final UserValidator userValidator ; public UserService(UserValidator userValidator) { this.userValidator = userValidator; } public void someServiceMethod(User user) { Errors errors = new BeanPropertyBindingResult(user, "user"); userValidator.validate(user, errors); if (errors.hasErrors()) { // 如果存在错误,则进行错误处理 } }}
2.2 编程式验证示例
首先,我们定义如下实体对象:
public class Employee { private Long id; private String name; private String email; private Department department; // getters, setters}public class Department { private Long id; private String name; // getters, setters}
定义验证器
public class EmployeeValidator implements Validator { @Override public boolean supports(Class<?> clazz) { return Employee.class.isAssignableFrom(clazz); } @Override public void validate(Object target, Errors errors) { ValidationUtils.rejectIfEmpty(errors, "id", "id.empty"); ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "姓名不能为空"); ValidationUtils.rejectIfEmptyOrWhitespace(errors, "email", "邮件不能为空"); Employee employee = (Employee) target; if (employee.getName() != null && employee.getName().length() < 2) { errors.rejectValue("name", "姓名必须大于等于2个字符"); } }}
同样地,我们也为 Department 类定义了相应的验证器。如有需要,你可以添加更复杂的验证规则。
public class DepartmentValidator implements Validator { @Override public boolean supports(Class<?> clazz) { return Department.class.equals(clazz); } @Override public void validate(Object target, Errors errors) { ValidationUtils.rejectIfEmpty(errors, "id", , "id.empty"); ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "部门名称不能为空"); Department department = (Department) target; if(department.getName() != null && department.getName().length() < 2) { errors.rejectValue("name", "部门名称必须大于等于2个字符"); } }}
现在,我们可以按照如下方式对 Employee 和 Department 类的实例进行验证:
Employee employee = new Employee();EmployeeValidator employeeValidator = new EmployeeValidator();Errors errors = new BeanPropertyBindingResult(employee, "employee");employeeValidator.validate(employee, errors);if (!errors.hasErrors()) { System.out.println("Object is valid");} else { for (FieldError error : errors.getFieldErrors()) { System.out.println(error.getCode()); }}
输出结果
id.empty姓名不能为空邮件不能为空
2.3 链式多验证器
如果使用上述自定义验证器时,仅对 Employee 对象进行验证,则 API 不会自动验证与之关联的 Department 对象。正常情况下,在验证某个特定对象时,应同时对其所有关联对象执行验证。
编程式验证 API 支持通过调用其他验证器来聚合所有错误,并最终返回综合结果。这可通过 ValidationUtils.invokeValidator() 方法实现,如下示例:
public class EmployeeValidator implements Validator { DepartmentValidator departmentValidator; public EmployeeValidator(DepartmentValidator departmentValidator) { if (departmentValidator == null) { throw new IllegalArgumentException("The supplied Validator is null."); } if (!departmentValidator.supports(Department.class)) { throw new IllegalArgumentException("The supplied Validator must support the Department instances."); } this.departmentValidator = departmentValidator; } @Override public void validate(Object target, Errors errors) { //... try { errors.pushNestedPath("department"); ValidationUtils.invokeValidator(this.departmentValidator, employee.getDepartment(), errors); } finally { errors.popNestedPath(); } }}
说明:
- pushNestedPath() 方法允许为子对象设置临时嵌套路径。在上述示例中,当对 department 对象进行验证时,路径会被设置为 'employee.department'。
- popNestedPath() 方法则会在调用 pushNestedPath() 方法后,将路径重置回原始路径。在上述示例中,它会将路径重新恢复为 'employee'。
修改上面的代码如下:
public class EmployeeValidator implements Validator {
private final DepartmentValidator departmentValidator ; @Override public void validate(Object target, Errors errors) { // ... try { errors.pushNestedPath("department"); ValidationUtils.invokeValidator(this.departmentValidator, employee.getDepartment(), errors); } finally { errors.popNestedPath(); } }}
修改测试类
Department department = new Department() ;Employee employee = new Employee(null, null, null, department);EmployeeValidator employeeValidator = new EmployeeValidator(new DepartmentValidator());Errors errors = new BeanPropertyBindingResult(employee, "employee");employeeValidator.validate(employee, errors);if (!errors.hasErrors()) { System.out.println("Object is valid");} else { for (FieldError error : errors.getFieldErrors()) { System.out.println(error.getField() + "," + error.getCode()) ; }}
输出结果
id,id.emptyname,姓名不能为空email,邮件不能为空department.id,id.emptydepartment.name,部门名称不能为空
2.4 与Controller接口结合
将编程式验证器(Programmatic Validators)与Spring MVC控制器集成,需要将编程式验证器注入到控制器中、在Spring上下文中进行配置,并利用 @Valid 注解和 BindingResult 对象实现简洁的验证流程。
@RestController@RequestMapping("/employees")public class EmployeeController { @InitBinder protected void initBinder(WebDataBinder binder) { // 设置我们自定义的验证器 binder.setValidator(new EmployeeValidator(new DepartmentValidator())); } @PostMapping("/create") public ResponseEntity<?> create( @Validated @RequestBody Employee employee, BindingResult error) { if (error.hasErrors()) { List<String> errors = error.getFieldErrors() .stream() .map(err -> err.getField() + ", " + err.getCode()) .toList() ; return ResponseEntity.ok(errors) ; } return ResponseEntity.ok("success") ; }}
如上示例,验证时,将会应用我们自定义的验证器。
如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、在看。
关注公众号:woniuxgg,在公众号中回复:笔记 就可以获得蜗牛为你精心准备的java实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!