Affects: 6.0.0-RC2

We found this problem when testing Spring Boot's JacksonTester support but I believe it's a general AOT problem to do with factory beans that produce a generic type. Here's a small sample that reproduces the problem:

factory-bean-problem.zip

./gradlew test should result in two failures:

> Task :test FAILED

FactoryBeanProblemApplicationTests > contextLoads() FAILED
    org.springframework.beans.factory.UnsatisfiedDependencyException at AutowiredAnnotationBeanPostProcessor.java:744
        Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException at DefaultListableBeanFactory.java:1782

MinimalReproductionTests > aotConfig() FAILED
    org.springframework.beans.factory.UnsatisfiedDependencyException at MinimalReproductionTests.java:46
        Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException at MinimalReproductionTests.java:46

The first reproduces the problem that we've seen in Spring Boot. If you edit build.gradle to comment out the setting of the spring.aot.enabled system property and run ./gradlew test again, contextLoads() should pass. The second is an attempt to reproduce the problem in a minimal (and slightly simplified) manner. It doesn't involve Boot or the Test Framework at all.

From what I have seen in the debugger, the behavior diverges in GenericTypeAwareAutowireCandidateResolver.checkGenericTypeMatch(BeanDefinitionHolder, DependencyDescriptor). In the Java config case, the target type is determined via a BeanFactory.getType call:

https://github.com/spring-projects/spring-framework/blob/997dd3ee65b0e30b396fa06db75eb6a80b3809bc/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java#L107-L110

This is sufficient for the result of the check to be true and for autowiring to succeed.

In the AOT-processed case, the target type is available from the bean definition:

https://github.com/spring-projects/spring-framework/blob/997dd3ee65b0e30b396fa06db75eb6a80b3809bc/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java#L90

As a result, the getType call on the bean factory never happens. The result of the check is false and autowiring fails.

Comment From: sdeleuze

I tentatively add this one for 6.0 GA since that breaking some propular use cases.

Comment From: wilkinsona

Things have improved, but the problem isn't fully resolved. While the slightly simplified reproducer now passes, the other test in the sample still fails:

> Task :test FAILED

FactoryBeanProblemApplicationTests > contextLoads() FAILED
    org.springframework.beans.factory.UnsatisfiedDependencyException at AutowiredAnnotationBeanPostProcessor.java:744
        Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException at DefaultListableBeanFactory.java:1812

3 tests completed, 1 failed

FAILURE: Build failed with an exception.

As before, this test passes when AOT processing is not enabled.

Comment From: wilkinsona

With the recent changes, ResolvableType.isAssignableFrom(ResolvableType, Map<Type, Type>) is now called with this being org.springframework.boot.test.json.JacksonTester<com.example.demo.Request> and other being org.springframework.boot.test.json.JacksonTester<java.lang.Object>. false is returned as the generics do not align as com.example.demo.Request is not assignable from java.lang.Object.

Comment From: wilkinsona

This failure to match the generics can be reproduced by changing the type of the @Autowired field in Consumer to JacksonTester<Request>:

static class Consumer {

    @Autowired
    JacksonTester<Request> tester;

}

This then fails as com.example.demo.MinimalReproductionTests$Request is not assignable from java.lang.Object.

Comment From: jhoeller

Thanks, Andy. This is probably not related to the original FactoryBean problem anymore but rather to an AOT difference with our fallback matching for incomplete generics (we had similar reports elsewhere). Since this is the last known AOT problem for GA, I'm trying to sort this out for good today.

Comment From: jhoeller

Andy, with the injection point changed as above, if the AOT-determined target type is then also set to FactoryBean<JacksonTester<Request>>, your revised test passes fine. So this is literally about JacksonTester<Object> not being assignable to JacksonTester<Request> now - a different problem where our lenient fallback matching for generics only kicks in if no target type has been pre-determined.

I do wonder why we keep having such non-matching generics to begin with. In an ideal world, we would not need the lenient fallback match at all anymore. That said, I'm currently looking into how to let it kick in even for a pre-determined target type.

Comment From: jhoeller

I got an arrangement for this now where for pre-determined FactoryBean types, we only take the resolved Class in a fallback match, which is equivalent to how lazy type determination for FactoryBeans proceeds in a fallback scenario. I'll push this shortly.

Comment From: jhoeller

@wilkinsona This should be addressed in the upcoming snapshot now. Please let me know whether it works for Boot...