Closed HzjNeverStop closed 4 months ago
@HzjNeverStop Are you able to provide a sample application? I'm having a hard time following the example you're describing. Swallowing the exception is intentional since we assume that if the class isn't available there can be no beans of that type.
@HzjNeverStop Are you able to provide a sample application? I'm having a hard time following the example you're describing. Swallowing the exception is intentional since we assume that if the class isn't available there can be no beans of that type.
I tried to reproduce this issue, which occurs when the method return type is a generic structure. You can reproduce this using the github demo (after packaging with mvn package, start the SpringBoot program using java -jar).
Thanks, @HzjNeverStop. Something's definitely wrong here. The sample has:
@AutoConfiguration(before = DemoBAutoConfiguration.class)
public class DemoAAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public DemoBean demoBean() {
return new DemoBean("from DemoAAutoConfiguration");
}
}
and
@AutoConfiguration
public class DemoBAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public DemoBean demoBean() {
return new DemoBean("from DemoBAutoConfiguration");
}
}
Yet both demoBean
methods result in the bean being defined. The condition evaluation report shows that no beans of that type were found both times it was evaluated:
DemoAAutoConfiguration#demoBean matched:
- @ConditionalOnMissingBean (types: com.example.spring_demo.DemoBean; SearchStrategy: all) did not find any beans (OnBeanCondition)
DemoBAutoConfiguration#demoBean matched:
- @ConditionalOnMissingBean (types: com.example.spring_demo.DemoBean; SearchStrategy: all) did not find any beans (OnBeanCondition)
Stack trace of the exception that's swallowed:
java.lang.NoClassDefFoundError: javax/servlet/Filter
at java.base/java.lang.ClassLoader.defineClass1(Native Method)
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1017)
at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150)
at java.base/java.net.URLClassLoader.defineClass(URLClassLoader.java:524)
at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:427)
at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:421)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:712)
at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:420)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:592)
at org.springframework.boot.loader.net.protocol.jar.JarUrlClassLoader.loadClass(JarUrlClassLoader.java:103)
at org.springframework.boot.loader.launch.LaunchedClassLoader.loadClass(LaunchedClassLoader.java:91)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:467)
at java.base/sun.reflect.generics.factory.CoreReflectionFactory.makeNamedType(CoreReflectionFactory.java:114)
at java.base/sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:125)
at java.base/sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49)
at java.base/sun.reflect.generics.visitor.Reifier.reifyTypeArguments(Reifier.java:68)
at java.base/sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:138)
at java.base/sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49)
at java.base/sun.reflect.generics.repository.MethodRepository.computeReturnType(MethodRepository.java:75)
at java.base/sun.reflect.generics.repository.MethodRepository.getReturnType(MethodRepository.java:66)
at java.base/java.lang.reflect.Method.getGenericReturnType(Method.java:295)
at org.springframework.core.MethodParameter.getGenericParameterType(MethodParameter.java:516)
at org.springframework.core.SerializableTypeWrapper$MethodParameterTypeProvider.getType(SerializableTypeWrapper.java:297)
at org.springframework.core.SerializableTypeWrapper.forTypeProvider(SerializableTypeWrapper.java:106)
at org.springframework.core.ResolvableType.forType(ResolvableType.java:1458)
at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1379)
at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1361)
at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1328)
at org.springframework.core.ResolvableType.forMethodReturnType(ResolvableType.java:1273)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryMethod(AbstractAutowireCapableBeanFactory.java:817)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineTargetType(AbstractAutowireCapableBeanFactory.java:682)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:653)
at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:1687)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:562)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:534)
at org.springframework.boot.autoconfigure.condition.OnBeanCondition.collectBeanNamesForType(OnBeanCondition.java:247)
at org.springframework.boot.autoconfigure.condition.OnBeanCondition.getBeanNamesForType(OnBeanCondition.java:240)
at org.springframework.boot.autoconfigure.condition.OnBeanCondition.getBeanNamesForType(OnBeanCondition.java:230)
at org.springframework.boot.autoconfigure.condition.OnBeanCondition.getMatchingBeans(OnBeanCondition.java:183)
at org.springframework.boot.autoconfigure.condition.OnBeanCondition.getMatchOutcome(OnBeanCondition.java:158)
at org.springframework.boot.autoconfigure.condition.SpringBootCondition.matches(SpringBootCondition.java:47)
at org.springframework.context.annotation.ConditionEvaluator.shouldSkip(ConditionEvaluator.java:108)
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForBeanMethod(ConfigurationClassBeanDefinitionReader.java:183)
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:144)
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:120)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:429)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:290)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:788)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:606)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:335)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352)
at com.example.spring_demo.SpringDemoApplication.main(SpringDemoApplication.java:10)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:91)
at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:53)
at org.springframework.boot.loader.launch.JarLauncher.main(JarLauncher.java:58)
Caused by: java.lang.ClassNotFoundException: javax.servlet.Filter
at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:445)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:592)
at org.springframework.boot.loader.net.protocol.jar.JarUrlClassLoader.loadClass(JarUrlClassLoader.java:103)
at org.springframework.boot.loader.launch.LaunchedClassLoader.loadClass(LaunchedClassLoader.java:91)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525)
... 66 more
I've tried patching Framework so that AbstractAutowireCapableBeanFactory.getTypeForFactoryMethod
catches NoClassDefFoundError
and returns null
. This seems aligned with its javadoc that states that it will return "the type for the bean if determinable, or null
otherwise". With this change in place, the sample then fails like this:
2024-06-20T09:49:47.393+01:00 ERROR 46247 --- [spring-demo] [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'errorBean' defined in class path resource [com/example/spring_demo/DemoCAutoConfiguration.class]: Failed to instantiate [org.springframework.boot.web.servlet.FilterRegistrationBean]: Factory method 'errorBean' threw exception with message: javax/servlet/Filter
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:648) ~[spring-beans-6.1.8.jar!/:6.1.8]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:485) ~[spring-beans-6.1.8.jar!/:6.1.8]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1341) ~[!/:6.1.8]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1171) ~[!/:6.1.8]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:561) ~[!/:6.1.8]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521) ~[!/:6.1.8]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) ~[spring-beans-6.1.8.jar!/:6.1.8]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.8.jar!/:6.1.8]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) ~[spring-beans-6.1.8.jar!/:6.1.8]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.1.8.jar!/:6.1.8]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:975) ~[spring-beans-6.1.8.jar!/:6.1.8]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:962) ~[spring-context-6.1.8.jar!/:6.1.8]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624) ~[spring-context-6.1.8.jar!/:6.1.8]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-3.2.6.jar!/:3.2.6]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[spring-boot-3.2.6.jar!/:3.2.6]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) ~[spring-boot-3.2.6.jar!/:3.2.6]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363) ~[spring-boot-3.2.6.jar!/:3.2.6]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352) ~[spring-boot-3.2.6.jar!/:3.2.6]
at com.example.spring_demo.SpringDemoApplication.main(SpringDemoApplication.java:10) ~[!/:0.0.1-SNAPSHOT]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:91) ~[spring-demo-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:53) ~[spring-demo-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
at org.springframework.boot.loader.launch.JarLauncher.main(JarLauncher.java:58) ~[spring-demo-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.web.servlet.FilterRegistrationBean]: Factory method 'errorBean' threw exception with message: javax/servlet/Filter
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:177) ~[spring-beans-6.1.8.jar!/:6.1.8]
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:644) ~[spring-beans-6.1.8.jar!/:6.1.8]
... 25 common frames omitted
Caused by: java.lang.NoClassDefFoundError: javax/servlet/Filter
at java.base/java.lang.ClassLoader.defineClass1(Native Method) ~[na:na]
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1017) ~[na:na]
at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150) ~[na:na]
at java.base/java.net.URLClassLoader.defineClass(URLClassLoader.java:524) ~[na:na]
at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:427) ~[na:na]
at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:421) ~[na:na]
at java.base/java.security.AccessController.doPrivileged(AccessController.java:712) ~[na:na]
at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:420) ~[na:na]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:592) ~[na:na]
at org.springframework.boot.loader.net.protocol.jar.JarUrlClassLoader.loadClass(JarUrlClassLoader.java:103) ~[spring-demo-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
at org.springframework.boot.loader.launch.LaunchedClassLoader.loadClass(LaunchedClassLoader.java:91) ~[spring-demo-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) ~[na:na]
at com.example.spring_demo.DemoCAutoConfiguration.errorBean(DemoCAutoConfiguration.java:17) ~[!/:0.0.1-SNAPSHOT]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) ~[spring-beans-6.1.8.jar!/:6.1.8]
... 26 common frames omitted
Caused by: java.lang.ClassNotFoundException: javax.servlet.Filter
at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:445) ~[na:na]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:592) ~[na:na]
at org.springframework.boot.loader.net.protocol.jar.JarUrlClassLoader.loadClass(JarUrlClassLoader.java:103) ~[spring-demo-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
at org.springframework.boot.loader.launch.LaunchedClassLoader.loadClass(LaunchedClassLoader.java:91) ~[spring-demo-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) ~[na:na]
... 44 common frames omitted
I think this is exactly what we want. Unlike the exception above that's swallowed, the failure here precisely pinpoints the cause of the problem as it identifies errorBean
as the cause.
We'll transfer this to the Framework team so that they can consider if they want to make a change here.
Environment: SpringBoot 3.2.6
In the following scenario, this behavior makes it difficult to locate the root cause of the problem: For example, two Configuration classes A and B register two BeanDefinitions A and B with the same beanName, using @ConditionalOnMissingBean to ensure that only one BeanDefinition is effective.
When another Configuration class C registers a BeanDefinition, and this BeanDefinition uses a class that cannot be found, the following situation may occur:
As a result, the BeanDefinition B registered by Configuration class B is not skipped, causing springboot to throw a BeanDefinitionOverrideException. The root cause exception of the problem is swallowed in the OnBeanCondition#getBeanNamesForType method, making it difficult to locate the problem.
The swallowed stack trace:
The spring boot failure reason: