使用自定义注解构造时验证Record组件值的问题
Micronaut中Record组件自定义注解验证不触发的问题解决
问题原因
Micronaut的Bean验证机制默认不会介入纯Java方式直接实例化Record的过程:
- 控制器参数能触发验证,是因为框架在请求参数绑定的流程中,会自动触发验证逻辑;
- 而直接通过
new MailAddress("invalid")创建Record对象时,属于原生Java实例化,Micronaut的验证器不会主动介入这个过程,即便你给Record组件加了@EMail注解也不会触发。
解决方案
1. 主动使用Validator手动验证
注入jakarta.validation.Validator实例,在创建Record后手动校验对象,不符合规则时抛出异常:
import jakarta.inject.Inject; import jakarta.validation.Validator; import jakarta.validation.ConstraintViolation; import java.util.Set; public class UserService { @Inject Validator validator; public UserId createValidMailAddress(String email) { MailAddress mailAddress = new MailAddress(email); Set<ConstraintViolation<MailAddress>> violations = validator.validate(mailAddress); if (!violations.isEmpty()) { throw new IllegalArgumentException("邮箱格式无效: " + violations.iterator().next().getMessage()); } return mailAddress; } }
2. 通过Micronaut Bean容器创建Record
让Record通过Micronaut的BeanContext创建,框架会自动触发验证流程:
import io.micronaut.context.BeanContext; import jakarta.inject.Inject; public class UserService { @Inject BeanContext beanContext; public UserId createValidMailAddress(String email) { // 通过BeanContext创建实例,自动触发注解验证 return beanContext.createBean(MailAddress.class, email); } }
3. 自定义Record构造器并绑定验证
显式编写Record的构造器,注入Validator并在构造时完成验证(注意此方式仍需通过Bean容器创建实例,无法直接new):
@Introspected public sealed interface UserId { record Identifier(@Positive long value) implements UserId {} record MailAddress(@EMail String value) implements UserId { @Inject public MailAddress(String value, Validator validator) { this.value = value; Set<ConstraintViolation<MailAddress>> violations = validator.validate(this); if (!violations.isEmpty()) { throw new ConstraintViolationException(violations); } } } }
内容的提问来源于stack exchange,提问作者hunsricker
相关产品推荐
相关产品推荐

