You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring MVC:在REST控制器中注册自定义Validator的正确方式

Hey, let's break down the core validation mechanisms in Spring that you're looking into—here's a clear breakdown of each approach and when to use them:

Spring 验证机制核心实现方式梳理

1. JSR-303/JSR-380 注解式校验(javax.validation.constraints)

这是最常用的简单对象字段校验方案,属于Java EE(现Jakarta EE)标准,Spring对它提供了无缝支持。你只需要在实体类的字段上标注对应的约束注解,Spring就能自动触发校验逻辑。

适合场景:单个字段的基础校验(比如非空、长度范围、格式匹配等)。

示例代码:

public class UserDTO {
    @NotNull(message = "用户名不能为空")
    @Size(min = 3, max = 20, message = "用户名长度需在3-20之间")
    private String username;

    @Email(message = "邮箱格式不正确")
    private String email;

    // getter/setter 方法
}

在Spring控制器中,只需给参数加上@Valid或@Validated注解即可触发校验:

@PostMapping("/users")
public ResponseEntity<?> createUser(@Valid @RequestBody UserDTO userDTO) {
    // 执行业务逻辑
}

2. 自定义类型级注解校验

当你需要基于多个字段联动的复杂校验时,单字段注解就无法满足需求了。这种情况下可以自定义类型级别的约束注解,实现跨字段的逻辑校验。

适合场景:校验密码与确认密码是否一致、起始时间早于结束时间等多字段关联的场景。

示例代码:
首先定义自定义注解:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PasswordMatchValidator.class)
public @interface PasswordMatch {
    String message() default "密码与确认密码不匹配";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

然后实现对应的校验器:

public class PasswordMatchValidator implements ConstraintValidator<PasswordMatch, UserDTO> {
    @Override
    public boolean isValid(UserDTO userDTO, ConstraintValidatorContext context) {
        return userDTO.getPassword() != null && userDTO.getPassword().equals(userDTO.getConfirmPassword());
    }
}

最后在实体类上标注该注解:

@PasswordMatch
public class UserDTO {
    private String password;
    private String confirmPassword;
    // 其他字段
}

3. Spring 原生 org.springframework.validation.Validator 接口实现

这是Spring提供的原生校验方式,完全脱离JSR标准,适合需要依赖Spring上下文或复杂业务逻辑的校验场景。你需要实现Validator接口,自定义完整的校验逻辑。

适合场景:校验某个字段的值是否在数据库中已存在(需调用DAO层)、基于动态配置的校验规则等。

示例代码:

public class UserValidator implements Validator {
    @Autowired
    private UserRepository userRepository;

    @Override
    public boolean supports(Class<?> clazz) {
        return UserDTO.class.isAssignableFrom(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {
        UserDTO userDTO = (UserDTO) target;
        // 校验用户名是否已存在
        if (userRepository.existsByUsername(userDTO.getUsername())) {
            errors.rejectValue("username", "username.exists", "该用户名已被注册");
        }
        // 其他自定义校验逻辑
    }
}

在控制器中可以手动调用校验,或者配置为全局校验器:

@PostMapping("/users")
public ResponseEntity<?> createUser(@RequestBody UserDTO userDTO, Errors errors) {
    userValidator.validate(userDTO, errors);
    if (errors.hasErrors()) {
        return ResponseEntity.badRequest().body(errors.getAllErrors());
    }
    // 执行业务逻辑
}

内容的提问来源于stack exchange,提问作者dmitryb

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 08:21:06