Obtaining a bean provided by a configuration class that has dependencies caused trouble during AOT processing.
The Boot Configuration for JdbcRepositoriesAutoConfiguration
contains a conditional configuration class SpringBootJdbcConfiguration
depending on the ApplicationContext
.
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(AbstractJdbcConfiguration.class)
static class SpringBootJdbcConfiguration extends AbstractJdbcConfiguration {
private final ApplicationContext applicationContext;
SpringBootJdbcConfiguration(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Override
protected Set<Class<?>> getInitialEntitySet() throws ClassNotFoundException {
return new EntityScanner(this.applicationContext).scan(Table.class);
}
}
The AbstractJdbcConfiguration
exposes a bean of type RelationalManagedTypes
@Bean
public RelationalManagedTypes jdbcManagedTypes() throws ClassNotFoundException {
return RelationalManagedTypes.fromIterable(getInitialEntitySet());
}
Obtaining jdbcManagedTypes
via registeredBean.getBeanFactory().getBean(registeredBean.getBeanName(), ManagedTypes.class))
within a BeanRegistrationAotProcessor
errors with
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration$SpringBootJdbcConfiguration': Failed to instantiate [org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration$SpringBootJdbcConfiguration]: No default constructor found
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1305)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1197)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:566)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:526)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:393)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1323)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1160)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:566)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:526)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:205)
at org.springframework.data.aot.ManagedTypesBeanRegistrationAotProcessor.processAheadOfTime(ManagedTypesBeanRegistrationAotProcessor.java:57)
Comment From: snicoll
I am not sure I got all those pieces together. Can you please share how I can reproduce this myself? Also, if that exception below is happening at build time, I don't think that's right. We shouldn't be instantiating beans at build time like that.
Comment From: christophstrobl
@snicoll build either the data-jdbc
or data-cassandra
sample from this PR spring-projects-experimental/spring-native#1664
Comment From: snicoll
Can you clarify that the bean is instantiated at build time and why that is ?
Comment From: christophstrobl
The bean serves as an entry point for domain type inspection of the data module which will add reflection configuration etc.
Comment From: snicoll
What happens is that InstantiationAwareBeanPostProcessor
is not honored when a GenericApplicationContext
is refreshed for AOT processing. As a result, determineCandidateConstructors
is not invoked and the bean definition does not contain the constructor to use.
We're in a weird state right now as we're not actively preventing beans to be instantiated, but the configuration of the BeanFactory
is not complete to do so properly.
Comment From: snicoll
I have some code that supports the use case described above. This also includes honoring autowired injection points as well as initialization in case the @Configuration
class needs to check something before producing beans.
I've kept the very conservative approach to only include MergedBeanDefinitionPostProcessor
instances which happen to be the same than we need for the above. Now that those are registered as post processors, the context will try to post process the bean definition again, unless a package-level postProcessed
flag is set on the RootBeanDefinition
. Unfortunately, that logic is in a separate module so I don't know yet how to fix that.
Current code (and test) are available in 35da93c
Comment From: OlgaMaciaszek
Possibly related issue is happening with Spring Cloud LoadBalancer (in this case, the issue is only present for native images - works fine on the JVM):
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'test.LoadBalancerClientSpecification': Unexpected exception during bean creation
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:539) ~[card-service:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[card-service:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[card-service:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[card-service:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[card-service:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:930) ~[card-service:6.0.0-SNAPSHOT]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:926) ~[card-service:6.0.0-SNAPSHOT]
[...]
Caused by: org.springframework.beans.TypeMismatchException: Failed to convert value of type 'java.lang.String[]' to required type 'java.lang.Class[]'; Could not find class [io.github.olgamaciaszek.cardservice.lb.CustomLoadBalancerConfiguration]
at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:79) ~[card-service:6.0.0-SNAPSHOT]
at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:45) ~[card-service:6.0.0-SNAPSHOT]
at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveArgument(BeanInstanceSupplier.java:327) ~[na:na]
at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveArguments(BeanInstanceSupplier.java:265) ~[na:na]
at org.springframework.beans.factory.aot.BeanInstanceSupplier.get(BeanInstanceSupplier.java:208) ~[na:na]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainInstanceFromSupplier(AbstractAutowireCapableBeanFactory.java:1224) ~[card-service:6.0.0-SNAPSHOT]
Comment From: snicoll
Having tried to fix the problem above in several ways, we've decided to resort to a double invocation of MBDPP if a bean is instantiated at build-time.