2
votes

Suppose that I have two beans defined like this:

@Configuration
public class ConfigurationA {

    @Bean
    @Qualifier("restTemplateA")
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();

        //setting some restTemplate properties

        return restTemplate;
    }
}

@Configuration
public class ConfigurationB {

    @Bean
    @Qualifier("restTemplateB")
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();

        //setting some restTemplate properties

        return restTemplate;
    }
}

When I autowire restTemplate without providing @Qualifier, which bean will be injected? How is it decided?

@Service
public class someClass {

    @Autowired
    private RestTemplate restTemplate;

} 

Note:When I test, one of the bean is injected. It didn't lead any exception.

Note2: I know I can use @Qualifier to inject the bean that I want or I can use @Primary to avoid ambiguity. But I just want to understand why it is valid for Spring.

Note3: When I changed the method name in class ConfigurationB to restTemplateB, bean defined in ConfigurationA is injected. Again it didnt' lead any exception.

I tried this in Spring Boot version 1.4.4

3
@Bean( name = "firstRestTemplateName" ) then when autowiring into your service add @Qualifier( "firstRestTemplateName" )RobOhRob
You can also annotate a bean with @Primary to make it the defaultRobOhRob
@RobOhRob thx for your comment. I edited the question.H.Ç.T
Did you try to create two beans in the same configuration class?Hatice
It will result in NoSuchBeanDefinitionException . I assume someClass is not autodetected by springR.G

3 Answers

1
votes

I think what you will find, is if you run your application, it will log an error like requried a single bean but 2 were found.

What you can do however is remove the ambiguity using the @Qualifier where you need it injected and naming your bean definitions, i.e. for your example.

@Configuration
public class Configuration {

    @Bean(name="restTemplateA")
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();

        //setting some restTemplate properties

        return restTemplate;
    }

    @Bean(name="restTemplateB")
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();

        //setting some restTemplate properties

        return restTemplate;
    }
}

Then when you come to injecting and using the templates

@Service
public class someClass {

    @Autowired
    @Qualifer("restTemplateA")
    private RestTemplate restTemplate;

} 

However, you can also mark one of the templates as a Primary with @Primary, and this bean will then be used in each place you do not qualify your autowired.

    @Bean(name="restTemplateA")
    @Primary
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();

        //setting some restTemplate properties

        return restTemplate;
    }
0
votes

Actually, you cannot have both configuration classes, because you'll get a bean name conflict. To fix this, rename the method name:

@Bean
    @Qualifier("restTemplateB")
    public RestTemplate restTemplateB() {
        RestTemplate restTemplate = new RestTemplate();

        //setting some restTemplate properties

        return restTemplate;
    }

This way, two RestTemplate will be created with restTemplate and restTemplateB name respectively, and the first one will be injected in the service class.

0
votes

Case1: Same method (bean) names in configuration classes.

@Configuration
public class ConfigurationA {

    @Bean
    @Qualifier("restTemplateA")
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();

        //setting some restTemplate properties

        return restTemplate;
    }
}

@Configuration
public class ConfigurationB {

    @Bean
    @Qualifier("restTemplateB")
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();

        //setting some restTemplate properties

        return restTemplate;
    }
}

Result: restTemplate in ConfigrationB is injected. restTemplate in ConfiguratioA is overridden by restTemplate in ConfigurationB as can be found in the logs:

o.s.b.f.s.DefaultListableBeanFactory : Overriding bean definition for bean 'restTemplate' with a different definition: replacing ...

Important Note: Bean overriding is disabled by default from spring boot version 2.1 (you can check this link)

Case2: Changing method name in ConfigurationB to restTemplateB.

@Configuration
public class ConfigurationA {

    @Bean
    @Qualifier("restTemplateA")
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();

        //setting some restTemplate properties

        return restTemplate;
    }
}

@Configuration
public class ConfigurationB {

    @Bean
    @Qualifier("restTemplateB")
    public RestTemplate restTemplateB() {
        RestTemplate restTemplate = new RestTemplate();

        //setting some restTemplate properties

        return restTemplate;
    }
}

Result: Both beans in ConfigurationA an ConfigurationB are created. Bean in ConfigurationA is injected. Because bean name will be used if a by-type autowiring doesn't find a single matching bean. Check this for more information