jbwt
jbwt

Reputation: 394

Why does Spring Boot create beans of the same type regardless of any @Primary annotation?

I have a Spring Configuration class that looks like:

@Configuration
public class MyDependencyConfig {

@Bean
@Primary
public MyDependency getMyDependency(){
    System.out.println("getMyDependency");
    return Mockito.mock(MyDependency.class);
}

@Bean
public MyDependency getMyDependency2(){
    System.out.println("getMyDependency2");
    return Mockito.mock(MyDependency.class);
}
}

And have elsewhere in my code:

@Autowired
MyDependency foo

Why is it when the application context starts up, my console prints

getMyDependency

getMyDependency2

when only the bean from getMyDependency() will be used? I am using spring boot-1.5.1.RELEASE

Thank you kindly, Jason

Upvotes: 1

Views: 2053

Answers (1)

davidxxx
davidxxx

Reputation: 131376

The Spring configuration instantiates and loads all declared and required beans in the Spring container at the startup of the Spring context.

So, even if you don't use all beans defined in your configuration, the methods annotated @Bean in your configuration will be all invoked.

The @Primary annotation has another goal.

It indicates that a bean should be given preference when multiple candidates are qualified to autowire a single-valued dependency. If exactly one 'primary' bean exists among the candidates, it will be the autowired value.

It is just a way to not specify systematically the @Qualifier annotation when you have more than one candidate bean.

For example in your case as one of two beans is specified as primary :

@Bean
@Primary
public MyDependency getMyDependency(){
    System.out.println("getMyDependency");
    return Mockito.mock(MyDependency.class);
}

You don't need to specify the qualifier "getMyDependency" to inject which one specified as primary :

@Autowired
@Qualifier("getMyDependency")
MyDependency foo;

You can directly do that :

@Autowired
MyDependency foo;

While for the second one bean that is not specified as @Primary :

@Bean
public MyDependency getMyDependency2(){
    System.out.println("getMyDependency2");
    return Mockito.mock(MyDependency.class);
}

You have to specify @Qualifier to clear ambiguities when you want to inject it :

@Autowired
@Qualifier("getMyDependency2")
MyDependency foo;

Loading beans in a lazy way is not advised as it delays the catch of configuration errors. So, it is eager by default.

You have more details in the link provided in your comment.
Now, if you want to prevent this default behavior and define a lazy initialization for a bean, you can alternatively specify the lazy String value in the @Scope annotation of the bean :

@Bean 
@Scope("lazy")
public MyDependency getMyDependency(){
  ...
}

or better you can annotate the bean declaration or all the configuration (if you want that all beans be lazy initialized) with a @Lazy annotation.

For a specific bean :

@Bean 
@Lazy
public MyDependency getMyDependency(){
   ...
}

For all beans of the configuration :

@Lazy
@Configuration
public class MyDependencyConfig {
   ...
}

Upvotes: 9

Related Questions