@Component beans override @Bean ones for collection autowiring

Viewed 59

I've got a very simple spring boot test application.

It has just a Dog class and the @SpringBootApplication annotated one. I create two beans of Dog and everything runs as expected.


public class Dog {
    
    public String name;
    
    public Dog() {
        this("noname");
    }

    public Dog(String name) {
        this.name = name;
    }

    public String toString() {
        return name;
    }
}

@SpringBootApplication
public class DemoApplication {

    @Autowired
    private List<Dog> dogs;
    
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
    
    @Bean
    CommandLineRunner runner1() {
        return args -> {
            for (Dog d : dogs) {
                System.out.println(d);
            }
        };
    }

    
    @Bean
    Dog laika() {
        return new Dog("laika");
    }

    @Bean
    Dog lassie() {
        return new Dog("lassie");
    }
}

Outputs:

laika
lassie

However now I add a @Component annotation to Dog class, expecting that now I'll got three beans of type Dog and that's what it seems to happen if I print all beans with another CommandLineRunner like this one:

    @Bean
      public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
        return args -> {
          System.out.println("Let's inspect the beans provided by Spring Boot:");
          String[] beanNames = ctx.getBeanDefinitionNames();
          Arrays.sort(beanNames);
          for (String beanName : beanNames) {
            System.out.println(beanName);
          }
        };
    }

Outputs:

Let's inspect the beans provided by Spring Boot:
applicationAvailability
applicationTaskExecutor
commandLineRunner
demoApplication
dog
laika
lassie
lifecycleProcessor
...

Yet, when I use my first CommandLineRunner for printing out the contents of my Dog list, the only output that I get is:

noname

It seems like @Component beans have made the beans declared by @Bean dissapear for collection injection. I observe the same behaviour with any beans, for example if I declare more CommandLineRunners in an independent @Component class, everyone is ran, but when I @Autowire'd them in a list, only the ones declared with @Component are injected.

Nevertheless, I can still use the other Dog beans. For example, if I annotate Laika bean with @Primary, it's the one that will be injected as a method argument, but nothing changes regarding the @Autowire'd collection.

1 Answers

Your boot application is in an interesting way, but anyway, this pcs of code

    @Bean
CommandLineRunner runner1() {
    return args -> {
        for (Dog d : dogs) {
            System.out.println(d);
        }
    };
}

when it is invoking, the other 2 dogs are not yet initialized, so in the dogs list you only have the @component annotated one, which is "noname"

Related