Description
Ibrahim Ghazal opened SPR-15856 and commented
Trying to create a LocalValidatorFactoryBean
bean while both Hibernate Validator 4.3.2 and Bean Validation 1.1 are on the classpath throws the below error:
Aug 09, 2017 1:11:57 PM org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5197848c: startup date [Wed Aug 09 13:11:57 AST 2017]; root of context hierarchy
Aug 09, 2017 1:11:57 PM org.hibernate.validator.internal.util.Version <clinit>
INFO: HV000001: Hibernate Validator 4.3.2.Final
Aug 09, 2017 1:11:57 PM org.springframework.context.annotation.AnnotationConfigApplicationContext refresh
WARNING: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'localValidatorFactoryBean' defined in demo.Demo: Invocation of init method failed; nested exception is java.lang.AbstractMethodError: org.hibernate.validator.internal.engine.ConfigurationImpl.getDefaultParameterNameProvider()Ljavax/validation/ParameterNameProvider;
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'localValidatorFactoryBean' defined in demo.Demo: Invocation of init method failed; nested exception is java.lang.AbstractMethodError: org.hibernate.validator.internal.engine.ConfigurationImpl.getDefaultParameterNameProvider()Ljavax/validation/ParameterNameProvider;
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1628)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)
at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:84)
at demo.Demo.main(Demo.java:11)
Caused by: java.lang.AbstractMethodError: org.hibernate.validator.internal.engine.ConfigurationImpl.getDefaultParameterNameProvider()Ljavax/validation/ParameterNameProvider;
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:216)
at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:201)
at org.springframework.validation.beanvalidation.LocalValidatorFactoryBean.configureParameterNameProviderIfPossible(LocalValidatorFactoryBean.java:315)
at org.springframework.validation.beanvalidation.LocalValidatorFactoryBean.afterPropertiesSet(LocalValidatorFactoryBean.java:284)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624)
... 11 more
The issue is that LocalValidatorFactoryBean
tries to detect Bean Validation 1.1 using the API interfaces, regardless of what the provider actually implements (See here and here). And since Class.getMethod
succeeds, ReflectionUtils.invokeMethod
throws an AbstractMethodError
, which is an Error
and not an Exception
, so it doesn't get caught by the catch here.
This could be fixed by explicitly catching AbstractMethodError
in addition to Exception
. But I think the correct fix is to call getMethod
on the provider class instead of the API interfaces.
(The reason I can't simply remove Bean Validation 1.1 from the classpath is that Spring Boot includes it even if <hibernate-validator.version>4.3.2.Final</hibernate-validator.version>
is set in pom.xml. We also faced the same issue when deploying to a Java EE container that implements Bean Validation 1.1.)
This is only an issue for Spring 4.x. Spring 5 will make Bean Validation 1.1 mandatory.
To reproduce:
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.3.2.Final</version>
</dependency>
</dependencies>
</project>
src/main/java/demo/Demo.java
package demo;
import javax.validation.Validator;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
public class Demo {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Demo.class);
Validator validator = context.getBean(Validator.class);
System.out.println(validator);
context.close();
}
@Bean
public LocalValidatorFactoryBean localValidatorFactoryBean() {
LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();
return localValidatorFactoryBean;
}
}
Affects: 4.3.10
Issue Links:
- Follow-up: AbstractMethodError when calling validated method of MethodValidationPostProcessor is using a @Lazy validator [SPR-15807] #20362 Follow-up: AbstractMethodError when calling validated method of MethodValidationPostProcessor is using a
@Lazy
validator - Error on type argument constraint validation failure [SPR-15916] #20470 Error on type argument constraint validation failure
Referenced from: commits 0d0399a