Spring Boot中使用ConstraintValidator时是否支持修改字段值?
实现说明
首先要明确:ConstraintValidator的isValid()方法返回值为布尔类型,设计定位是判定字段值是否符合校验规则,按照Bean Validation规范,校验器不应该修改被校验对象的原始值,因此无法直接通过重写isValid()实现「入参字符串、返回处理后字符串替换原值」的逻辑。
要实现字符串首尾空白自动裁剪、null值直接返回的需求,根据使用场景选择对应实现即可:
方案1:接口入参场景(最常用)
如果是Controller层接收前端参数的场景,直接在Jackson反序列化阶段完成值处理,再搭配校验注解做规则判定即可,处理后的值会直接绑定到字段上,后续业务逻辑拿到的就是裁剪后的结果。
- 编写自定义字符串反序列化器实现裁剪逻辑
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer; import java.io.IOException; public class TrimStringDeserializer extends StdScalarDeserializer<String> { protected TrimStringDeserializer() { super(String.class); } @Override public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { String rawValue = p.getValueAsString(); // null直接返回,非空值裁剪首尾空白 return rawValue == null ? null : rawValue.strip(); } }
- 自定义
@Trimmed注解,绑定反序列化器和校验器
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import jakarta.validation.Constraint; import jakarta.validation.Payload; import java.lang.annotation.*; @Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented @Constraint(validatedBy = TrimmedValidator.class) @JsonDeserialize(using = TrimStringDeserializer.class) public @interface Trimmed { String message() default "字段不能为纯空白字符"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
- 编写对应校验器做规则判定
import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; public class TrimmedValidator implements ConstraintValidator<Trimmed, String> { @Override public boolean isValid(String value, ConstraintValidatorContext context) { // 反序列化阶段已经完成trim,这里null直接放行,非空值判断是否为纯空白 if (value == null) { return true; } return !value.isEmpty(); } }
使用时直接在对应字符串字段上加注解即可,可搭配@NotBlank等其他校验注解共同使用:
public class UserCreateRequest { @Trimmed @NotBlank(message = "用户名不能为空") private String username; }
方案2:JPA实体入库场景
如果是需要在数据库入库前对实体字符串字段做裁剪,直接使用JPA生命周期回调实现即可,不需要依赖校验器:
- 编写实体监听器,在入库、更新前遍历字段做裁剪
import jakarta.persistence.PrePersist; import jakarta.persistence.PreUpdate; import java.lang.reflect.Field; public class StringTrimListener { @PrePersist @PreUpdate public void trimAllStringFields(Object entity) throws IllegalAccessException { for (Field field : entity.getClass().getDeclaredFields()) { if (field.getType().equals(String.class)) { field.setAccessible(true); String originalVal = (String) field.get(entity); if (originalVal != null) { field.set(entity, originalVal.strip()); } } } } }
- 在需要自动裁剪的实体类上添加监听器注解即可生效
import jakarta.persistence.Entity; import jakarta.persistence.EntityListeners; @Entity @EntityListeners(StringTrimListener.class) public class UserEntity { private String username; // 其他字段... }
注意:不要尝试在
isValid()方法中直接通过反射修改被校验对象的字段值,这种写法违反Bean Validation的设计规范,在多次校验、分组校验等场景下会出现不可预期的副作用。
内容的提问来源于stack exchange,提问作者user19254373
相关产品推荐
相关产品推荐

