Why do we use a qualifier when we can have a name for the bean?

3.3k views Asked by At

Why do we use qualifiers with @Bean when we can have different names for different beans of the same type (class)?

@Bean
@Qualifier("fooConfig")
public Baz method1() {

}

Isn't the following code more clean?

@Bean("fooConfig")
public Baz method1() {

}

If I create two beans of the same type with different names (using @Bean annotation), then can we inject them specifically using the @Qualifier annotation(can be added on field/constructor parameter/setter) in another bean?


@Bean("fooConfig")
public Baz method1(){

}

@Bean("barConfig")
public Baz method2(){

}

// constructor parameter of a different bean
final @Qualifier("fooConfig") Baz myConfig

If the above is true, then where do we use @Qualifier (with @Bean or @Component) instead of giving the bean a name as shown below?


@Bean
@Qualifier("fooConfig")
public Baz method1(){

}

@Bean
@Qualifier("barConfig")
public Baz method2(){

}

// constructor parameter of a different bean
final @Qualifier("fooConfig") Baz myConfig
3

There are 3 answers

1
AudioBubble On BEST ANSWER
  1. Beans have names. They don't have qualifiers. @Qualifier is annotation, with which you tell Spring the name of Bean to be injected.

  1. No.

  1. Default Qualifier is the only implementation of the interface(example is below, 4th question) or the only method with a particular return type. You don't need to specify the @Qualifier in that case. Spring is smart enough to find itself.

For example:

@Configuration
public class MyConfiguration {
    @Bean
    public MyCustomComponent myComponent() {
        return new MyCustomComponent();
    }
}

If you will try to inject myComponent somewhere, Spring is smart enough to find the bean above. Becaude there is only one Bean with return type MyCustomComponent. But if there was a couple of methods, that would return MyCustomComponent, then you would have to tell Spring which one to inject with @Qualifier annotation.

SIDENOTE: @Bean annotation by default Spring uses the method name as a bean name. You can also assign other name like @Bean("otherComponent").


  1. You have one Interface, and a couple of Classes implementing it. You inject bean of your interface. How can Spring know which Class should be used?

This is you interface:

public interface TestRepository{}

This is your implementation 1:

@Repository
public class Test1Repository implements TestRepository{}

Your implementation 2:

@Repository
public class Test2Repository implements TestRepository{}

Now you are injecting it like:

private final TestRepository testRepository;

public TestServiceImpl(TestRepository testRepository) {
    this.testRepository= testRepository;
}

QUESTION! How is Spring supposed to know which class to inject? Test1 or Test2? That's why you tell it with @Qualifier which class.

private final TestRepository testRepository;

public TestServiceImpl(@Qualifier("test1Repository") TestRepository testRepository) {
    this.testRepository= testRepository;
}
1
LunaLissa On

I Prefer different method to not using @Qualifier

  1. Create common Interface
public interface CommonFooBar{
public String commonFoo();
public String commonBar();
}
  1. Extends to each service
public interface FooService extends CommonFooBar {

}

public interface BarService extends CommonFooBar {

}
  1. Then using it to your class
@Autowired
FooService fooService;

or

@Autowired
BarService barService;

so, we can defined the single responsibility to each interface and This kind of segregation is more readable to every junior.

0
Andrew Harrison On

I quite like a different way of working. Surely if you provide a unique name for your bean, then that is all you need?

Given the example below, its easy to see that Spring will name the beans based on the method name used to create the beans. In other words, if you give your beans sensible names, then the code should become self-explanatory. This also works when injecting beans into other classes.

The end result of this is:

  • Spring will name your beans based on the method used to create them.
  • If you import a bean, Spring will try to match on the bean name.
  • If you try to import a bean that does not match the name, Spring will attempt to match the class.
  • If your injected field name does not match the bean name and there are more than one instance of your bean, Spring will throw an exception on startup as it won't know which one to inject.

Lets not over-complicate Spring.

@Bean
mqConnectionFactory() {
    ConnectionFactory connectionFactory = new MQXAConnectionFactory();
    return connectionFactory;
}

@Bean 
public ConnectionFactory pooledConnectionFactory(ConnectionFactory mqconnectionFactory) {
    JmsPoolConnectionFactory connectionFactory = new JmsPoolConnectionFactory();
    connectionFactory.setConnectionFactory(mqConnectionFactory);
    return connectionFactory;
}

@Bean 
public ConnectionFactory cachingConnectionFactory(ConnectionFactory mqConnectionFactory) {
    CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
    connectionFactory.setTargetConnectionFactory(mqConnectionFactory);
    return connectionFactory;
} 

@Bean
public JmsTemplate jmsTemplate(ConnectionFactory cachingConnectionFactory) {
    JmsTemplate jmsTemplate = new JmsTemplate();
    jmsTemplate.setConnectionFactory(cachingConnectionFactory);
    return jmsTemplate;
}

@Bean
public DefaultMessageListenerContainer messageListenerContainer(ConnectionFactory pooledConnectionFactory) {
    DefaultMessageListenerContainer container = new DefaultMessageListenerContainer();
    container.setConnectionFactory(pooledConnectionFactory);
    ...
    return container;
}