Rafid
Rafid

Reputation: 20189

How to specify sub-dependency for AutoWired Beans

I have a Spring component defined like this:

@Component
public class SearchIndexImpl implements SearchIndex {
    IndexUpdater indexUpdater;

    @Autowired
    public SearchIndexImpl(final IndexUpdater indexUpdater) {
        Preconditions.checkNotNull(indexUpdater);
        this.indexUpdater = indexUpdater;
    }
}

along with two implementations of the IndexUpdater interface, like:

@Component
public class IndexDirectUpdater implements IndexUpdater, DisposableBean, InitializingBean {

}

@Component
public class IndexQueueUpdater implements IndexUpdater, DisposableBean, InitializingBean {
}

If I try to auto-wire SearchIndexImpl like this:

@Autowired
private SearchIndex searchIndex;

I get the following exception:

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'IndexUpdater' available: expected single matching bean but found 2: indexDirectUpdater,indexQueueUpdater

which is expected since Spring cannot tell which IndexUpdater implementation to auto-wire for the indexUpdater parameter in the constructor of SearchIndexImpl. How do I guide Spring to the bean that it should use? I understand I can use the @Qualifier annotation, but that will hard-code the index updater to one of the implementation, while I want the user to be able to specify what index updater to use. In XML, I can do something like:

<bean id="searchIndexWithDirectUpdater" class="SearchIndexImpl"> 
    <constructor-arg index="0" ref="indexDirectUpdater"/>
</bean>

How do I do the same using Spring's Java annotations?

Upvotes: 0

Views: 549

Answers (1)

davidxxx
davidxxx

Reputation: 131346

Use the @Qualifier annotation to specify the dependency to use :

public SearchIndexImpl(@Qualifier("indexDirectUpdater") IndexUpdater indexUpdater) {
    Preconditions.checkNotNull(indexUpdater);
    this.indexUpdater = indexUpdater;
}

Note that @Autowired is not needed to autowire the arg constructor of a bean since Spring 4.


To answer to your comment.

To let the class that will use the bean to define the dependency to use you could allow it to define the IndexUpdater instance to inject in the container such as :

// @Component not required any longer
public class IndexDirectUpdater implements IndexUpdater, DisposableBean, InitializingBean {

}

// @Component not required any longer
public class IndexQueueUpdater implements IndexUpdater, DisposableBean, InitializingBean {
}

Declare the bean in a @Configuration class :

@Configuration
public class MyConfiguration{

@Bean
public IndexUpdater getIndexUpdater(){
     return new IndexDirectUpdater();
}

The SearchIndexImpl bean will now resolve the dependency thanks to IndexUpdater getIndexUpdater().
Here we use @Component for one bean and @Bean for its dependency.
But we could also allow a full control on the beans to instantiate by using only @Bean and by removing @Component on the 3 classes :

@Configuration
public class MyConfiguration{

@Bean
public IndexUpdater getIndexUpdater(){
     return new IndexDirectUpdater();
}

@Bean 
public SearchIndexImpl getSearchIndexFoo(){
     return new SearchIndexImpl(getIndexUpdater());
}

Upvotes: 1

Related Questions