自定义约束注解
创建自定义 @UniqueEmail 注解,并实现 ConstraintValidator
自定义约束注解 是 CoddyKit 上的免费 Java Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Java Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Java Academy 课程共包含 4 节课。
标准约束不足时
标准 Bean 验证注解可以覆盖常见场景。对于特定于领域的规则(例如唯一电子邮件、有效 IBAN 和密码强度),请创建自定义约束注解。
定义约束注解
使用 @Constraint(validatedBy = ...) 创建注解,并提供必需的消息、分组和负载元素,以及适当的保留策略和目标。
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
@Documented
@Constraint(validatedBy = UniqueEmailValidator.class)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface UniqueEmail {
String message() default "Email already registered";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}实现 ConstraintValidator
实现 ConstraintValidator<UniqueEmail, String>。初始化方法读取注解元素;isValid 执行实际检查。
@Component
public class UniqueEmailValidator implements ConstraintValidator<UniqueEmail, String> {
@Autowired private UserRepository repo;
@Override
public boolean isValid(String email, ConstraintValidatorContext ctx) {
if (email == null) return true; // let @NotBlank handle null
return !repo.existsByEmail(email);
}
}使用自定义约束
像使用标准约束一样,将该注解应用于字段、参数或返回值。
public record CreateUserRequest(
@NotBlank
String name,
@NotBlank @Email @UniqueEmail
String email
) {}使用插值生成自定义消息
使用 {elementName} 在消息中引用注解元素。使用 ctx.buildConstraintViolationWithTemplate(),通过动态数据添加自定义消息。
@Constraint(validatedBy = RangeValidator.class)
public @interface InRange {
int min() default 0;
int max() default 100;
String message() default "Must be between {min} and {max}";
// ...
}类级别约束
在类级别应用约束,以便同时验证多个字段,这对于密码确认等跨字段规则非常有用。
@Documented
@Constraint(validatedBy = PasswordMatchValidator.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface PasswordMatch {
String message() default "Passwords do not match";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}类级别验证器实现
验证器会接收整个对象。访问多个字段即可执行跨字段验证。
public class PasswordMatchValidator implements ConstraintValidator<PasswordMatch, ChangePasswordRequest> {
public boolean isValid(ChangePasswordRequest req, ConstraintValidatorContext ctx) {
if (req.getNewPassword() == null) return true;
boolean match = req.getNewPassword().equals(req.getConfirmPassword());
if (!match) {
ctx.disableDefaultConstraintViolation();
ctx.buildConstraintViolationWithTemplate("Passwords do not match")
.addPropertyNode("confirmPassword").addConstraintViolation();
}
return match;
}
}组合约束
通过在自定义注解上添加现有约束来创建元约束。两者都会自动应用。
@NotBlank
@Email
@Size(max = 255)
@Constraint(validatedBy = {})
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidEmail {
String message() default "Invalid email";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}返回多个违规项
使用 ctx.disableDefaultConstraintViolation(),在一次验证器执行过程中为发现的每个问题添加单独的违规消息。
public boolean isValid(String password, ConstraintValidatorContext ctx) {
List<String> issues = new ArrayList<>();
if (password.length() < 8) issues.add("Too short");
if (!password.matches(".*\\d.*")) issues.add("Must contain a digit");
if (issues.isEmpty()) return true;
ctx.disableDefaultConstraintViolation();
issues.forEach(msg -> ctx.buildConstraintViolationWithTemplate(msg).addConstraintViolation());
return false;
}由 Spring 管理的验证器
由于 UniqueEmailValidator 使用 @Component 注解,Spring 会向其中注入仓储。只有通过 Spring 的验证器引导该验证器时才有效;Spring Boot 会自动完成此配置。
测试自定义验证器
直接对验证器进行单元测试,不需要 Spring 上下文即可测试纯验证逻辑。请使用 Validation.buildDefaultValidatorFactory() 进行集成测试。
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
CreateUserRequest req = new CreateUserRequest("", "bad-email", null);
Set<ConstraintViolation<CreateUserRequest>> violations = validator.validate(req);
System.out.println(violations.size()); // 3快速检查
自定义约束验证器实现哪个接口?
回顾
使用 @interface 和 @Constraint(validatedBy=...) 创建注解。实现 ConstraintValidator。使用 @Component 进行 Spring 注入。类级别约束可以同时验证多个字段。组合现有约束以避免重复。
常见问题解答
「自定义约束注解」课时是免费的吗?
是的 — 「自定义约束注解」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Java Academy 课程的其余内容,请升级到 CoddyKit PRO。 Java Academy 课程共包含 4 节课。
「自定义约束注解」这节课中我会学到什么?
创建自定义 @UniqueEmail 注解,并实现 ConstraintValidator 你通过在浏览器中直接运行的动手代码来练习 Java Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Java Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Java Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「自定义约束注解」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Java Academy 课中编写并运行代码吗?
能。每节 Java Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。