Spring 中的 Hibernate Validator 使用不同的 ConstraintValidatorManager 启动
Hibernate Validator in Spring Boot using different ConstraintValidatorManager
我遇到了之前在 Spring Boot vs. Hibernate Validation 中提到的问题,其中自定义约束验证器中的依赖项自动装配不起作用。从我自己的调试中,我注意到当实体级验证发生时,与 Hibernate 为表单提交执行 bean 验证时相比,Hibernate 加载不同的 ConstraintValidatorManager。后者工作正常,前者导致自定义约束验证器的依赖项为空。似乎 Hibernate 从根上下文加载一个管理器,从 servlet 上下文加载一个管理器。这可以解释 Hibernate 不知道自定义约束验证器中自动装配的依赖项的存在。但是,如果这是真的,我不明白发生了什么,或者如何让 Hibernate/JPA 知道 Spring 上下文和它的 beans。
我希望有人能指出我正确的方向?我已经尝试了以下所有答案,还有更多(例如不同的库版本、配置方法、通过 utils class 加载静态 bean 等):
Inject Repository inside ConstraintValidator with Spring 4 and message interpolation configuration
此外,我已经多次阅读 Spring 引导的参考指南,但运气不佳。有几个案例提到他们的 Hibernate 验证工作正常,既适用于常规 bean 提交,也适用于实体持久化期间。不幸的是,我似乎无法检索到他们使用的确切 (Java) 配置,但他们似乎使用的是默认配置。我开始怀疑这是否是一个特定的 Spring 引导问题(尽管它被声明为 Spring 验证和 Hibernate 验证的组合应该开箱即用)。
添加像下面这样的 bean 并不能解决问题(默认工厂是 SpringConstraintValidatorFactory ofcourse):
@Bean
public LocalValidatorFactoryBean validator()
{
LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
bean.setValidationMessageSource(messageSource());
return bean;
}
也不包含 Hibernate 验证器的 bean 定义:
加载和注入所需的 bean 有许多不同的方法,但是如果 Hibernate 根本不知道上下文中加载的 bean(因为它使用的是不同的上下文?),如何进行?
提前致谢。
更新:Gradle 文件
buildscript {
ext {
springBootVersion = '2.1.5.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group = '<hidden>'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
jcenter()
}
dependencies {
implementation('org.springframework.boot:spring-boot-starter-web')
implementation('org.springframework.boot:spring-boot-starter-tomcat:2.1.5.RELEASE')
implementation('org.springframework.boot:spring-boot-starter-thymeleaf')
implementation('org.springframework.boot:spring-boot-starter-security')
implementation('org.springframework.boot:spring-boot-starter-data-jpa')
implementation('org.springframework.boot:spring-boot-starter-mail')
implementation('org.springframework.session:spring-session-core')
annotationProcessor('org.springframework.boot:spring-boot-configuration-processor')
implementation('org.postgresql:postgresql')
// https://mvnrepository.com/artifact/org.jboss.aerogear/aerogear-otp-java
implementation('org.jboss.aerogear:aerogear-otp-java:1.0.0')
implementation('com.github.mkopylec:recaptcha-spring-boot-starter:2.2.0')
implementation('nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:2.0.5')
implementation('org.thymeleaf.extras:thymeleaf-extras-springsecurity3:3.0.4.RELEASE')
implementation('javax.enterprise:cdi-api:2.0')
runtimeOnly('org.springframework.boot:spring-boot-devtools')
testImplementation('org.springframework.boot:spring-boot-starter-test')
testImplementation('org.springframework.security:spring-security-test')
testImplementation 'org.mockito:mockito-core:2.27.0'
}
有一种方法可以通过设置 javax.persistence.validation.factory
来告诉 Hibernate 使用相同的验证器
@Configuration
@Lazy
class SpringValidatorConfiguration {
@Bean
@Lazy
public HibernatePropertiesCustomizer hibernatePropertiesCustomizer(final Validator validator) {
return new HibernatePropertiesCustomizer() {
@Override
public void customize(Map<String, Object> hibernateProperties) {
hibernateProperties.put("javax.persistence.validation.factory", validator);
}
};
}
}
这样就一切正常了。
关于修复,只是为 are/were 处理此类问题的其他人总结更多 extensive/integrated 答案,我的配置现在包含所有这些 bean:
@Bean
public LocalValidatorFactoryBean validator()
{
LocalValidatorFactoryBean validatorFactory = new LocalValidatorFactoryBean();
validatorFactory.setValidationMessageSource(messageSource());
return validatorFactory;
}
@Bean
public MessageSource messageSource()
{
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor()
{
MethodValidationPostProcessor methodValidationPostProcessor = new MethodValidationPostProcessor();
methodValidationPostProcessor.setValidator(validator());
return methodValidationPostProcessor;
}
@Bean
public HibernatePropertiesCustomizer hibernatePropertiesCustomizer()
{
return properties ->
{
properties.put("javax.persistence.validation.factory", validator());
// Add more properties here such as validation groups (see comment for SO example)
};
}
有关添加休眠验证组以梳理不同 life-cycle 事件(例如 bean 与实体)的验证的示例,请参阅
我遇到了之前在 Spring Boot vs. Hibernate Validation 中提到的问题,其中自定义约束验证器中的依赖项自动装配不起作用。从我自己的调试中,我注意到当实体级验证发生时,与 Hibernate 为表单提交执行 bean 验证时相比,Hibernate 加载不同的 ConstraintValidatorManager。后者工作正常,前者导致自定义约束验证器的依赖项为空。似乎 Hibernate 从根上下文加载一个管理器,从 servlet 上下文加载一个管理器。这可以解释 Hibernate 不知道自定义约束验证器中自动装配的依赖项的存在。但是,如果这是真的,我不明白发生了什么,或者如何让 Hibernate/JPA 知道 Spring 上下文和它的 beans。
我希望有人能指出我正确的方向?我已经尝试了以下所有答案,还有更多(例如不同的库版本、配置方法、通过 utils class 加载静态 bean 等):
Inject Repository inside ConstraintValidator with Spring 4 and message interpolation configuration
此外,我已经多次阅读 Spring 引导的参考指南,但运气不佳。有几个案例提到他们的 Hibernate 验证工作正常,既适用于常规 bean 提交,也适用于实体持久化期间。不幸的是,我似乎无法检索到他们使用的确切 (Java) 配置,但他们似乎使用的是默认配置。我开始怀疑这是否是一个特定的 Spring 引导问题(尽管它被声明为 Spring 验证和 Hibernate 验证的组合应该开箱即用)。
添加像下面这样的 bean 并不能解决问题(默认工厂是 SpringConstraintValidatorFactory ofcourse):
@Bean
public LocalValidatorFactoryBean validator()
{
LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
bean.setValidationMessageSource(messageSource());
return bean;
}
也不包含 Hibernate 验证器的 bean 定义:
加载和注入所需的 bean 有许多不同的方法,但是如果 Hibernate 根本不知道上下文中加载的 bean(因为它使用的是不同的上下文?),如何进行?
提前致谢。
更新:Gradle 文件
buildscript {
ext {
springBootVersion = '2.1.5.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group = '<hidden>'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
jcenter()
}
dependencies {
implementation('org.springframework.boot:spring-boot-starter-web')
implementation('org.springframework.boot:spring-boot-starter-tomcat:2.1.5.RELEASE')
implementation('org.springframework.boot:spring-boot-starter-thymeleaf')
implementation('org.springframework.boot:spring-boot-starter-security')
implementation('org.springframework.boot:spring-boot-starter-data-jpa')
implementation('org.springframework.boot:spring-boot-starter-mail')
implementation('org.springframework.session:spring-session-core')
annotationProcessor('org.springframework.boot:spring-boot-configuration-processor')
implementation('org.postgresql:postgresql')
// https://mvnrepository.com/artifact/org.jboss.aerogear/aerogear-otp-java
implementation('org.jboss.aerogear:aerogear-otp-java:1.0.0')
implementation('com.github.mkopylec:recaptcha-spring-boot-starter:2.2.0')
implementation('nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:2.0.5')
implementation('org.thymeleaf.extras:thymeleaf-extras-springsecurity3:3.0.4.RELEASE')
implementation('javax.enterprise:cdi-api:2.0')
runtimeOnly('org.springframework.boot:spring-boot-devtools')
testImplementation('org.springframework.boot:spring-boot-starter-test')
testImplementation('org.springframework.security:spring-security-test')
testImplementation 'org.mockito:mockito-core:2.27.0'
}
有一种方法可以通过设置 javax.persistence.validation.factory
@Configuration
@Lazy
class SpringValidatorConfiguration {
@Bean
@Lazy
public HibernatePropertiesCustomizer hibernatePropertiesCustomizer(final Validator validator) {
return new HibernatePropertiesCustomizer() {
@Override
public void customize(Map<String, Object> hibernateProperties) {
hibernateProperties.put("javax.persistence.validation.factory", validator);
}
};
}
}
这样就一切正常了。
关于修复,只是为 are/were 处理此类问题的其他人总结更多 extensive/integrated 答案,我的配置现在包含所有这些 bean:
@Bean
public LocalValidatorFactoryBean validator()
{
LocalValidatorFactoryBean validatorFactory = new LocalValidatorFactoryBean();
validatorFactory.setValidationMessageSource(messageSource());
return validatorFactory;
}
@Bean
public MessageSource messageSource()
{
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor()
{
MethodValidationPostProcessor methodValidationPostProcessor = new MethodValidationPostProcessor();
methodValidationPostProcessor.setValidator(validator());
return methodValidationPostProcessor;
}
@Bean
public HibernatePropertiesCustomizer hibernatePropertiesCustomizer()
{
return properties ->
{
properties.put("javax.persistence.validation.factory", validator());
// Add more properties here such as validation groups (see comment for SO example)
};
}
有关添加休眠验证组以梳理不同 life-cycle 事件(例如 bean 与实体)的验证的示例,请参阅