spring autowiring with unique beans: Spring expected single matching bean but found 2

SpringSpring MvcDependency InjectionAnnotationsAutowired

Spring Problem Overview


I am trying to autowire some beans (for dependency injection) using Spring for a webapp. One controller bean contains another bean which in turn holds a hashmap of another set of beans. For now the map only has one entry. When i run in tomcat and call the service I get an error saying that the second bean (held in the controller) is not unique

No unique bean of type [com.hp.it.km.search.web.suggestion.SuggestionService] is defined: expected single matching bean but found 2: [suggestionService, SuggestionService]

I cannot see where I am defining the bean twice however am new to Spring and autowiring so I may be missing something fundamental. Source code for xml and 2 class listed below...

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"     xmlns:context="http://www.springframework.org/schema/context"     xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
    http://www.springframework.org/schema/beans     
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

<context:component-scan base-package="com.hp.it.km.search.web.suggestion" />
<mvc:annotation-driven />
<context:annotation-config />

<bean id="SuggestionController" class="com.hp.it.km.search.web.suggestion.SuggestionController">
	<property name="service">
		<ref bean="SuggestionService" />
	</property>
</bean>

<bean id="SuggestionService" class="com.hp.it.km.search.web.suggestion.SuggestionService">
	<property name="indexSearchers"> 
		 <map>
    		<entry key="KMSearcher"> <ref bean="KMSearcherBean"></ref></entry>
		</map>
	</property>
</bean>

<bean id="KMSearcherBean" class="com.hp.it.km.search.web.suggestion.SuggestionIndexSearcher">
	  <constructor-arg index="0" value="KMSearcher" />
	  <constructor-arg index="1" value="C://dev//workspace//search-restful-webapp//src//main//resources//indexes//keyword" />
</bean>

The class asscoaites with the autowired controller and service bean are here...

@Controller
public class SuggestionController {
private SuggestionService service;

@Autowired
public void setService(SuggestionService service) {
	this.service = service;
}

public SuggestionService getService() {
	return service;
}

and...

@Component
public class SuggestionService {

private Map<String, IndexSearcher> indexSearchers = new HashMap<String,      IndexSearcher>();

@Autowired
public void setIndexSearchers(Map<String, IndexSearcher> indexSearchers) {
	this.indexSearchers = indexSearchers;
}

    public SuggestionService() {
	super(); }

Please Help!

Spring Solutions


Solution 1 - Spring

The issue is because you have a bean of type SuggestionService created through @Component annotation and also through the XML config . As explained by JB Nizet, this will lead to the creation of a bean with name 'suggestionService' created via @Component and another with name 'SuggestionService' created through XML .

When you refer SuggestionService by @Autowired, in your controller, Spring autowires "by type" by default and find two beans of type 'SuggestionService'

You could do one of the following

  1. Remove @Component from your Service and depend on mapping via XML - Easiest

  2. Remove SuggestionService from XML and autowire the dependencies - use util:map to inject the indexSearchers map.

  3. Use @Resource instead of @Autowired to pick the bean by its name .

     @Resource(name="suggestionService")
     private SuggestionService service;
    

or

    @Resource(name="SuggestionService")
    private SuggestionService service;

both should work.The third is a dirty fix and it's best to resolve the bean conflict through other ways.

Solution 2 - Spring

If you have 2 beans of the same class autowired to one class you shoud use @Qualifier (Spring Autowiring @Qualifier example).

But it seems like your problem comes from incorrect Java Syntax.

Your object should start with lower case letter

SuggestionService suggestion;

Your setter should start with lower case as well and object name should be with Upper case

public void setSuggestion(final Suggestion suggestion) {
    this.suggestion = suggestion;
}

Solution 3 - Spring

For me it was case of having two beans implementing the same interface. One was a fake ban for the sake of unit test which was conflicting with original bean. If we use

> @component("suggestionServicefake")

, it still references with suggestionService. So I removed @component and only used

> @Qualifier("suggestionServicefake")

which solved the problem

Solution 4 - Spring

If I'm not mistaken, the default bean name of a bean declared with @Component is the name of its class its first letter in lower-case. This means that

@Component
public class SuggestionService {

declares a bean of type SuggestionService, and of name suggestionService. It's equivalent to

@Component("suggestionService")
public class SuggestionService {

or to

<bean id="suggestionService" .../>

You're redefining another bean of the same type, but with a different name, in the XML:

<bean id="SuggestionService" class="com.hp.it.km.search.web.suggestion.SuggestionService">
    ...
</bean>

So, either specify the name of the bean in the annotation to be SuggestionService, or use the ID suggestionService in the XML (don't forget to also modify the <ref> element, or to remove it, since it isn't needed). In this case, the XML definition will override the annotation definition.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRob McFeelyView Question on Stackoverflow
Solution 1 - SpringAravind AView Answer on Stackoverflow
Solution 2 - Springdanny.lesnikView Answer on Stackoverflow
Solution 3 - SpringvsinghView Answer on Stackoverflow
Solution 4 - SpringJB NizetView Answer on Stackoverflow