JSR 303配置的核心在于引入Hibernate Validator依赖,在Spring容器中注册Validator实例,并配置消息源来定制验证结果。 这是基础配置和主体配置的骨架,其余都是围绕这个骨架展开,无论你是构建REST API还是传统Web应用,掌握这些配置都能让你在参数校验上少走弯路。
JSR 303基础配置怎么配?一步步教你搞定
添加Maven依赖
对于非Spring Boot项目,你需要手动添加Hibernate Validator依赖和Expression Language依赖,典型配置如下:
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.3.Final</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>3.0.1-b11</version>
</dependency>
如果你使用Spring Boot,只需引入spring-boot-starter-validation,它已经包含了所有必要依赖,包括Hibernate Validator和Tomcat的EL实现。
配置Validator Bean
在Spring配置中,定义一个LocalValidatorFactoryBean,并设置消息源:
@Bean
public LocalValidatorFactoryBean validator() {
LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
bean.setValidationMessageSource(messageSource());
return bean;
}
@Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource ms =
new ReloadableResourceBundleMessageSource();
ms.setBasename("classpath:ValidationMessages");
ms.setDefaultEncoding("UTF-8");
return ms;
}
这样,你就完成了基础配置,Spring会自动将ValidationMessages.properties中的消息绑定到验证注解。
XML配置方式
除了Java配置,你也可以使用Spring XML配置:
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="validationMessageSource" ref="messageSource"/>
</bean>
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:ValidationMessages"/>
<property name="defaultEncoding" value="UTF-8"/>
</bean>
两种方式效果相同,选择哪种取决于你的项目配置风格。
JSR 303主体配置:分组验证与自定义消息
定义实体约束
主体配置的核心是使用注解定义约束。
public class Product {
@NotNull(message = "{product.name.notnull}")
@Size(min = 1, max = 100)
private String name;
@DecimalMin(value = "0.01", message = "价格必须大于0")
private BigDecimal price;
}
这里,消息使用引用外部键,便于国际化。
管理验证消息
在classpath:ValidationMessages.properties中定义:
product.name.notnull=产品名称不能为空
javax.validation.constraints.Size.message={0}长度必须在{min}到{max}之间
对于中文环境,可以创建ValidationMessages_zh_CN.properties,编码为UTF-8,这样,验证器会根据Accept-Language自动切换消息,业内专家指出,这是企业级项目中最常见的配置方式。
分组验证配置
当需要在不同场景下应用不同验证规则时,使用分组。
定义分组接口:
public interface OnCreate {}
public interface OnUpdate {}
在实体上指定分组:
@NotNull(groups = OnCreate.class) @Null(groups = OnUpdate.class) private Long id;
Controller中使用@Validated(OnCreate.class):
@PostMapping
public ResponseEntity create(@Validated(OnCreate.class) @RequestBody Product product) {
// ...
}
分组验证是主体配置中非常实用的技巧,能避免同一个实体在不同接口中重复定义。
跨字段验证
有时需要验证多个字段的组合,比如密码和确认密码一致,可以使用@ScriptAssert或自定义类级别验证。@ScriptAssert示例:
@ScriptAssert(lang = "javascript", script = "_this.password == _this.confirmPassword", message = "密码不一致")
public class User {
private String password;
private String confirmPassword;
}
注意,@ScriptAssert需要引入JSR 223脚本引擎依赖,更推荐的方式是自定义类级别注解,通过isValid方法获取整个对象进行验证。
进阶配置:自定义注解与整合Spring Boot
实现自定义注解
当内置注解无法满足业务时,自定义注解是扩展验证的最佳方式,设计一个@Phone注解,验证手机号格式。
步骤:
- 定义注解接口,包含
@Constraint(validatedBy = PhoneValidator.class)。 - 实现
ConstraintValidator接口,编写验证逻辑。 - 在实体上使用
@Phone。
代码示例:
@Target({FIELD, PARAMETER})
@Retention(RUNTIME)
@Constraint(validatedBy = PhoneValidator.class)
public @interface Phone {
String message() default "手机号格式不正确";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class PhoneValidator implements ConstraintValidator<Phone, String> {
private static final Pattern PHONE_PATTERN =
Pattern.compile("^1[3-9]\d{9}$");
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) return true;
return PHONE_PATTERN.matcher(value).matches();
}
}
这样,你就拥有了一个可复用的手机号验证注解,自定义注解的开发周期通常不超过半天,但能显著提升代码复用性。
整合Spring Boot自动配置
在Spring Boot中,验证配置更加简化,你只需:
- 引入
spring-boot-starter-validation。 - 在Controller参数前加
@Valid或@Validated。 - 默认情况下,Spring Boot已经配置了
LocalValidatorFactoryBean,消息源默认为ValidationMessages。
如果需要自定义,可以通过application.properties修改消息源路径:
spring.messages.basename=validation/ValidationMessages
或者覆盖Validator Bean,据统计,Spring Boot的自动配置让JSR 303配置几乎零成本。
常见配置对比
| 配置项 | 基础配置 | 主体配置 |
|---|---|---|
| 依赖管理 | 手动添加Hibernate Validator和EL | 使用Spring Boot Starter或手动添加 |
| Bean定义 | 必须手动定义Validator Bean | 可依赖自动配置,也可自定义 |
| 消息源 | 通常硬编码在注解中 | 使用外部Properties文件,支持国际化 |
| 分组验证 | 较少使用 | 常用,通过@Validated指定分组 |
| 自定义注解 | 较少 | 灵活扩展,满足业务特化需求 |
掌握核心,灵活应用
JSR 303配置从基础依赖到主体验证规则,再到消息国际化和分组,构成了一个完整的参数校验体系。 掌握这些,你就能在项目中轻松实现规范、灵活的验证,减少重复代码,提升开发效率。
Q&A:JSR 303配置常见问题
Q1:JSR 303配置和Spring Boot冲突怎么办?
如果Spring Boot自动配置的Validator不满足需求,你可以通过@Primary或@Qualifier指定自定义的Validator Bean,Spring Boot的ValidationAutoConfiguration会检测到自定义配置并后退,你可以直接覆盖LocalValidatorFactoryBean,或使用@Bean定义Validator,在大多数情况下,覆盖Bean即可。
Q2:JSR 303分组验证配置需要改什么?
主要在Controller上使用@Validated注解指定分组,在实体类注解上通过groups属性指定分组,如果需要不同分组使用不同消息,可以在消息文件中使用{javax.validation.constraints.NotNull.message}之类的键,但分组本身不影响消息,只影响验证规则,行业共识认为,分组验证是处理复杂业务场景的利器。
Q3:JSR 303配置中如何实现自定义消息?
在ValidationMessages.properties中定义消息键,格式为约束类名.注解名.message,例如javax.validation.constraints.NotNull.message,然后实体注解中不写message属性,即可自动读取,注意字符编码和文件位置,默认放在classpath根目录下,如果使用Spring Boot,还可以通过spring.messages属性自定义消息文件位置。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/536660.html


