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.