Background: When trying to explore the source of dependency injection in the 5.2.2.RELEASE of the Spring Framework, I found a bug ((just a personal opinion)). Here is an example:

@Configuration
public class AtAutowiredAnnotationInjectionDemoV2 {

    @Autowired
    private Collection<UserEntity> userEntities;

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(AtAutowiredAnnotationInjectionDemoV2.class, BeanConfig.class);

        context.refresh();

        AtAutowiredAnnotationInjectionDemoV2 demo = context.getBean(AtAutowiredAnnotationInjectionDemoV2.class);
        System.out.println(demo.userEntities);

        context.close();
    }

    @Bean("user1")
    public UserEntity user1() {
        UserEntity userEntity = new UserEntity();
        userEntity.setId(1L);
        userEntity.setUsername("Markus Zhang");
        return userEntity;
    }

    @Bean("user2")
    @Primary
    public UserEntity user2() {
        UserEntity userEntity = new UserEntity();
        userEntity.setId(2L);
        userEntity.setUsername("Luna");
        return userEntity;
    }
}

@Configuration
public class BeanConfig {

    @Bean
    public UserEntity userByConfig() {
        UserEntity userEntity = new UserEntity();
        userEntity.setId(3L);
        userEntity.setUsername("markus zhang from bean config");
        return userEntity;
    }
}

public class UserEntity {
    private Long id;
    private String username;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public String toString() {
        return "UserEntity{" +
                "id=" + id +
                ", username='" + username + '\'' +
                '}';
    }
}

The code is Simple introduced as followed: UserEntity is a POJO, BeanConfig and AtAutowiredAnnotationInjectionDemoV2 are two configuration class, I start in the main function of AtAutowiredAnnotationInjectionDemoV2 AnnotationConfigApplicationContext and by dependency lookup Get the Spring Bean AtAutowiredAnnotationInjectionDemoV2, and print the users field, expectations should be three UserEntity Spring Bean,They are user1, user2, and userByConfig. But only the userByConfig Bean from the BeanConfig configuration class is actually injected. The result is as follows:

Spring Spring dependency Injection bug

Similarly, I modified the main function to remove the BeanConfig configuration class from the application context, such as in the following code:

@Configuration
public class AtAutowiredAnnotationInjectionDemoV2 {

    @Autowired
    private Collection<UserEntity> userEntities;

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // modified
        context.register(AtAutowiredAnnotationInjectionDemoV2.class);

        context.refresh();

        AtAutowiredAnnotationInjectionDemoV2 demo = context.getBean(AtAutowiredAnnotationInjectionDemoV2.class);
        System.out.println(demo.userEntities);

        context.close();
    }

    @Bean("user1")
    public UserEntity user1() {
        UserEntity userEntity = new UserEntity();
        userEntity.setId(1L);
        userEntity.setUsername("Markus Zhang");
        return userEntity;
    }

    @Bean("user2")
    @Primary
    public UserEntity user2() {
        UserEntity userEntity = new UserEntity();
        userEntity.setId(2L);
        userEntity.setUsername("Luna");
        return userEntity;
    }
}

The UserEntity annotated by the @Bean in the current class is injected, and the result is as follows:

Spring Spring dependency Injection bug

Tracing the source,I found that it took place at org.springframework.beans.factory.support.DefaultListableBeanFactory#findAutowireCandidates stage, Instead of injecting Spring beans from the current configuration class, it is returned early, resulting in only UserEntity beans that are not registered by the current configuration class being injected

Spring Spring dependency Injection bug

In summary, I think this case is a bug, the above is only a personal opinion, may not be correct. I am looking forward to your reply. Have a nice day!

Comment From: bclozel

Open Source support has expired for Spring Framework 5.2.x. Please reach out through the appropriate channels if you have a subscription.

We can reopen this issue if you manage to reproduce the problem with a supported version.

See https://spring.io/projects/spring-framework/#support

Comment From: markuszcl99

Hi,I re-executed the program using version 5.3.27 of the spring framework, but the result is the same as 5.2.x and does not inject the expected results into the fields in the configuration class. You can also refer to the following code:

public class UserEntity {
  private String username;

  public String getUsername() {
    return username;
  }

  public void setUsername(String username) {
    this.username = username;
  }

  @Override
  public String toString() {
    return "UserEntity{" +
        "username='" + username + '\'' +
        '}';
  }
}

@Configuration
public class BeanConfigA {

  @Autowired
  private Collection<UserEntity> userEntities;

  @Bean
  public UserEntity userEntityA1() {
    UserEntity userEntity = new UserEntity();
    userEntity.setUsername("User A1");
    return userEntity;
  }

  @Bean
  public UserEntity userEntityA2() {
    UserEntity userEntity = new UserEntity();
    userEntity.setUsername("User A2");
    return userEntity;
  }

  public Collection<UserEntity> getUserEntities() {
    return userEntities;
  }
}

@Configuration
public class BeanConfigB {

  @Autowired
  private Collection<UserEntity> userEntities;


  @Bean
  public UserEntity userEntityB1(){
    UserEntity userEntity = new UserEntity();
    userEntity.setUsername("User B1");
    return userEntity;
  }

  @Bean
  public UserEntity userEntityB2(){
    UserEntity userEntity = new UserEntity();
    userEntity.setUsername("User B2");
    return userEntity;
  }

  public Collection<UserEntity> getUserEntities() {
    return userEntities;
  }
}

public class DependencyInjectionBugDemo {
  public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    // register configuration class
    context.register(BeanConfigA.class, BeanConfigB.class);

    // refresh spring context
    context.refresh();

    Map<String, UserEntity> beansOfType = context.getBeansOfType(UserEntity.class);
    System.out.println("ioc container include : ");
    beansOfType.forEach((name, bean) -> System.out.println(name + ": " + bean.getUsername()));

    // The Spring Bean into which the userEntities field in BeanConfigA is injected is expected to contain User A1、User A2、User B1、User B2
    BeanConfigA beanConfigA = context.getBean(BeanConfigA.class);
    System.out.println(beanConfigA.getUserEntities());

    // The Spring Bean into which the userEntities field in BeanConfigB is injected is expected to contain User A1、User A2、User B1、User B2
    BeanConfigB beanConfigB = context.getBean(BeanConfigB.class);
    System.out.println(beanConfigB.getUserEntities());

    // close spring context
    context.close();
  }
}

The result is as follows:

Spring Spring dependency Injection bug

Comment From: snicoll

In summary, I think this case is a bug, the above is only a personal opinion, may not be correct.

This isn't a bug. You're stretching the tolerance of the container to its limit. It only work half way as you're showing in the sample because you're using field injection. If you remove the @Autowired on the fields and create a constructor that takes the collection this leads to:

Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'beanConfigA': Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'beanConfigB': Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'beanConfigA': Requested bean is currently in creation: Is there an unresolvable circular reference?
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:802)
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:241)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1354)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1191)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:561)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:975)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:960)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:625)
    at com.example.demoorder.DemoOrderApplication.main(DemoOrderApplication.java:15)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'beanConfigB': Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'beanConfigA': Requested bean is currently in creation: Is there an unresolvable circular reference?
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:802)
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:241)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1354)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1191)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:561)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:413)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1334)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1164)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:561)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1689)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1653)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1543)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1511)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1392)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1353)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:911)
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789)
    ... 13 more

If you try to implement the configuration arrangement that you have there manually (i.e. doing dependency injection manually) you'll see that it's impossible to achieve as collaborators must be honored (autowiring) before a configuration class can start producing beans.

In general, self injection is really a bad idea and should be avoided.

Comment From: markuszcl99

Ok, I get it. It seems that field injection is really not a recommended approach. Thanks for your reply