Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@Primary bean in one @Configuration class does not override a bean defined and used in another such class

Tags:

java

spring

Here's the class with the primary bean:

@Configuration
public class AppConfig {
    @Bean        
    @Primary
    public WeatherGauge weatherGauge() {
        return () -> "40 F";
    }
}

and here's the class defining and using the competing bean:

@Configuration
public class WeatherConfig {   
    @Bean
    public WeatherGauge weatherGauge() {
        return () -> "20 C";
    }
    @Bean
    public StateReporter stateReporter(WeatherGauge weatherGauge) {
        return new StateReporter(weatherGauge);
    }
}

I'm using spring-boot-starter-parent:2.1.9.RELEASE. If I use StateReporter and print the weather gauge, I get 20 C, which does not come from the primary bean. The @Primary is ignored. Is this by design or a flaw? Just the way @Configuration works? If I define the primary implementation as a @Component class, the @Primary is in fact honored.

Edit: I forgot to say that AppConfig gets picked up if the other bean is not present. Everything is in the same package as the main class and I do use the allow-override=true property.

like image 990
Programmer Trond Avatar asked Sep 13 '25 05:09

Programmer Trond


2 Answers

You're defining two beans with the same name and type: only one will be created, and the other will be overridden.

The @Primary annotation is for when two or more beans of the same type exist. It designates one of them as the primary bean used in dependency injection.

You can see how this works by making a small code change.

    @Bean
    @Primary
    public WeatherGauge weatherGauge2() {
        return new WeatherGauge("40 F");
    } 

    @Bean
    public WeatherGauge weatherGauge() {
        return new WeatherGauge("20 C");
    }

Now two beans are defined, with one of them weatherGauge2 being the primary.

like image 166
ck1 Avatar answered Sep 15 '25 19:09

ck1


I assumed that you used the prop:

spring.main.allow-bean-definition-overriding=true

to run this code. This link probably will clear your problem for you.

If you don't want to read the whole article:

The mechanism which caused you this problem is called bean overriding. It's almost impossible to predict which bean will override another with Java-based configs. When you use both (XML and Java based) configurations, then Java-based is always loaded first and XML configuration always latest, so it will override everything else. That's why your @Component class with @Primary is honored - because it is loaded after configuration.

like image 44
Raztyck Avatar answered Sep 15 '25 17:09

Raztyck