Search code examples
javaspringjavabeans

Spring 5 - custom qualifiers for @Bean methods - NoSuchBeanDefinitionException


I've been struggling a while now with using custom @Qualifier in conjunction with @Bean over the factory method. It looks like this:

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, 
ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface MyCustomQualifierUno {
    String value() default "";
}

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, 
ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface MyCustomQualifierDos {
    String value() default "";
}

And the factory class for desired beans consists of the below two methods:

@Bean
@MyCustomQualifierUno
public RestTemplate getRestTemplate(SomeConfigUno config,
    SomeErrorHandlerUno errorHandler) {
        return new RestTemplateBuilder()
            (...)
            .build();
}

@Bean
@MyCustomQualifierDos
public RestTemplate getRestTemplate(SomeConfigDos config,
    SomeErrorHandlerDos errorHandler) {
        return new RestTemplateBuilder()
            (...)
            .build();
}

Next, in the Client class I have a ctor injection done like this:

public SomeClientUno(@SomeQualifierUno RestTemplate 
    restTemplate) {
        this.restTemplate = restTemplate;
}

public SomeClientDos(@SomeQualifierDos RestTemplate 
    restTemplate) {
        this.restTemplate = restTemplate;
}

When I'm trying to run the app I get: NoSuchBeanDefinitionException

Quite intriguing tough is the fact, that when I add a dedicated class that extends the RestTemplate and placed there my @CustomQualifier I get exception, that there are actually two beans now (i.e. now it does consider the @Bean method)!

Running through: IntelliJ Ultimate 2019.1 OS: Windows 10 64bit Java: 1.8.0_191 Spring version: 5.1.9

PS. Those "Uno's and 'Dos'ses are meant to stress the fact, that those are some concrete types, quite irrelevant, but don't get confused with the "Uno's and 'Dos'ses in the @Qualifiers


Solution

  • This may seem too obvious, but why should you use Qualifiers at all? Let's assume you define your beans like this:

    @Bean
    public RestTemplate restTemplateUno(...) { ... }
    
    @Bean
    public RestTemplate restTemplateDos(...) { ... }
    

    This registers the beans with the qualifiers "restTemplateUno" and "restTemplateDos", because "By default, the bean name will be the same as the method name" (see Spring Docs for details).

    You could now inject them in the client constructors like this:

    @Autowired
    public SomeClientUno(RestTemplate restTemplateUno) {...}
    
    @Autowired
    public SomeClientDos(RestTemplate restTemplateDos) {...}
    

    I hope this helps and I didn't miss something obvious that caused you to consider using Qualifiers in the first place.