Affects: spring 5.3 (specifically; spring boot 2.6.6)

Sorry, I realize this is a report for an old version, but maybe this is still relevant in newer versions because google wasn't of much help to me

After changing CI server to build on linux with docker, spring boot app started to undeterministically fail with NoSuchBeanDefinitionException. Ie, it works half the time (and it used to work all the time when the jar was being built on windows). I tracked it down and I think the cause was probably this:

@Service
public class Foo {
  @Autowired public void setBar(BarImpl bar) { ... }
}

@Configuration
public class Config {
  @Bean 
  public Bar makeBar() {
    return new BarImpl();
  }
}

ie., a bean is registered through interface type, and autowired through implementation type. I think this is the cause, because I stepped through wiring internals with debugger, and I found container rejecting makeBar bean due to barImplType.isAssignableFrom(barType) yielding false (where type is ResolvedType instance).

This is bad code, but regardless if it should or shouldn't work, it should behave more deterministically.

Comment From: snicoll

it should or shouldn't work, it should behave more deterministically.

The order in which the Service and the Configuration classes are processed may not be deterministic, which would be the reason for the failure. Unfortunately, this is impossible for us to know without more details. If you want support from us, please share a small sample we can run ourselves that replicate the problem you've described.

Comment From: arvyy

Reproduced it on spring 6.0.8

https://github.com/arvyy/SpringIssue30359

Comment From: snicoll

@arvyy thanks for the reproducer. I am assuming you must be on Windows or something as I can easily reproduce it with MacOS without the loop and docker, see https://github.com/snicoll-scratches/SpringIssue30359

In general, you should not expose an interface type if the bean is to be injected by a specific subtype. Regardless of the outcome of this issue, I highly recommend fixing that.

What's happening is that the bean initialization is not deterministic. If svc1 is not yet initialized when ServiceUser1 is initialized, the container only knows about a bean of type Service1 and there's no bean of type ServiceImpl1 yet. We'll investigate how we can make the initialization order more deterministic so that it isn't OS-specific.

Comment From: jhoeller

The root concern are the two separate @Autowired methods on ServiceUser1: If setAnotherService gets resolved first, it works since AnotherService1 resolves a Service1 which implicitly makes the latter's actual bean class available. If setSvc gets resolved first, it fails since the ServiceImpl1 bean class has not been resolved yet.

To illustrate the case: a single setServices(AnotherService1 svc1, ServiceImpl1 svc2) method will always work, whereas a single setServices(ServiceImpl1 svc1, AnotherService1 svc2) method will always fail.

The root cause for this is the JVM using arbitrary method ordering on reflection, returning methods in a different order between runs. We address this with ASM-based method declaration sorting for multiple @Bean methods on the same configuration class already, maybe we should do the same for multiple @Autowired methods on a single class.