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!
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
Remove #Component from your Service and depend on mapping via XML - Easiest
Remove SuggestionService from XML and autowire the dependencies - use util:map to inject the indexSearchers map.
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.
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;
}
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
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.
Related
There is a spring componet with #Component annotation, it is just java class (not interface) with annotated #Autowired fields. I am trying to create mock like that:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
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.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"
>
<bean id="myComponent" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.MyComponent"/>
</bean>
</beans>
And got an exception that some of fields are not autowired. This happens, because when Spring see <constructor-arg value="com.MyComponent"/> it try to instantiate MyComponent bean and pass it to factory method.
I have tried to extract interface from component, in that case mocking works, but is there a way to make it working without extracting interface?
Also
I have tried adding type="java.lang.Class" but got same errors.
<bean id="myComponent" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg type="java.lang.Class" value="com.MyComponent"/>
</bean>
Any ideas?
Spring cannot autowire the bean because the type of the created bean is java.lang.Object and not com.myComponent. Apparently the order of the definitions in the XML matters as well.
Jayway has a very nice blog post about this: Spring Integration Tests - Creating Mock Objects
You can create a FactoryBean which returns the correct class:
public class MockitoFactoryBean<T> implements FactoryBean<T> {
private Class<T> classToBeMocked;
/**
* Creates a Mockito mock instance of the provided class.
* #param classToBeMocked The class to be mocked.
*/
public MockitoFactoryBean(Class<T> classToBeMocked) {
this.classToBeMocked = classToBeMocked;
}
#Override
public T getObject() throws Exception {
return Mockito.mock(classToBeMocked);
}
#Override
public Class<?> getObjectType() {
return classToBeMocked;
}
#Override
public boolean isSingleton() {
return true;
}
}
Using this factory bean, you can create a config like this:
<bean id="myComponent" class="com.yourpackage.MockitoFactoryBean">
<constructor-arg name="classToBeMocked" value="com.myComponent" />
</bean>
This will mock your component and the object type will com.myComponent.
Update
This is the original answer and explanation (using EasyMock): Autowiring of beans generated by EasyMock factory-method?
I have a spring rest application which has a Rest Controller as below
#RestController
public class IngestorController
{
#Autowired
private IngestorService ingestorService;
#RequestMapping( value = "/ingestor/createAndDeploy/{ingestorName}", method = RequestMethod.POST )
public void createAndDeploy( #PathVariable String ingestorName )
{
ingestorService.createAndDeploy( ingestorName );
}
}
Simlilarly I have a Service Bean as below
#Service
public class IngestorService
{
#Autowired
private IngestorCommandBuilder ingestorCommandBuilder;
private String uri;
private DeployTemplate deployTemplate;
public void init() throws URISyntaxException
{
deployTemplate = new DeployTemplate( new URI( uri ) );
}
#Transactional
public void createAndDeploy( Ingestor ingestor )
{
//.....
}
}
I have the Spring config as show below
<bean id="ingestorCommandBuilder" class="org.amaze.server.ingestor.IngestorCommandBuilder" />
<bean id="ingestorService" class="org.amaze.server.service.IngestorService" init-method="init">
<property name="uri" value="http://localhost:15217" />
</bean>
<bean id="ingestorController" class="org.amaze.server.controller.IngestorController"/>
When ever I try to start the application context the application context starts and it hits the init method in the IngestorService, deployTemplate object also initilized for the service bean.
But this bean is not autowired for the IngestorController. When I hit the rest endpoint from postman, the service bean has deployTemplate property as null.. The object that is assigned to the ingestorService variable in the Controller is a different object not the one which was called for the init method...
I tried making the service bean singleton (Even if the default scope is singleton) but dint work...
I am not able to find out the mistake I am doing.. Any suggestions appreciated...
If you use annotation-based configuration, you mostly dont need to describe all your beans in application context xml file. Annotations are all you need to autowire service.
To define your init method properly, use #PostConstruct annotation. Properties can be easily moved to externat .properties file and injected to your code with #Value annotation.
Alternatively, use #Qualifier with #Autowired.
First ensure that you have:
<context:annotation-config />
In your Spring config. Now you have severael alternatives:
<context:component-scan base-package="your.package.com" />
to scan for components, or
<!-- autowiring byName, bean name should be same as the property name annotate your ingestorservice with #Service("ingestorServiceByName") -->
<bean name="ingestorServiceByName" class="your.package.com.IngestorService" autowire="byName" />
<!-- autowiring byType, tif there are more bean of the same "general" type, this will not work and you will have to use qualifier or mark one bean as #Primary -->
<bean name="ingestorServiceByType" class="your.package.com.IngestorService" autowire="byType" />
<!-- autowiring by constructor is similar to type, but the DI will be done by constructor -->
<bean name="ingestorServiceConstructor" class="your.package.com.IngestorService" autowire="constructor" />
Please include your full Spring configuration to make it easier to analyze your problem.
When you are using Annotation based DI, you need not define the beans in XML.
#PostConstruct can be used to replace your init-method of xml config.
Just use
<context:component-scan base-package="..."/> <mvc:annotation-driven/>
I have the following controller defined:
#Controller
#RequestMapping("/test")
public class MyController extends AbstractController
{
#Autowired
public MyController(#Qualifier("anotherController") AnotherController anotherController))
{
...
}
}
I'm wondering if it's possible to use variables in the #Qualifier annotation, so that I can inject different controllers for different .properties files, e.g.:
#Controller
#RequestMapping("/test")
public class MyController extends AbstractController
{
#Autowired
public MyController(#Qualifier("${awesomeController}") AnotherController anotherController))
{
...
}
}
Whenever I try I get:
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No matching bean of type [com.example.MyController] found for dependency:
expected at least 1 bean which qualifies as autowire candidate for this
dependency. Dependency annotations:
{#org.springframework.beans.factory.annotation.Qualifier(value=${awesomeController})
I've included the following bean in my config.xml file:
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:config/application.properties</value>
</list>
</property>
</bean>
But the bean doesn't work unless I declare the bean explicitly in the xml file.
How do I do this with annotations??
First I think it is bad practice to make the dependency injection rely on configuration properties. You are probably going a wrong direction trying to do this.
However to answer your question: accessing placeHolder properties requires the dependency injection to be finished. To make sure it is, you can put your code that accesses the property inside a #PostContruct annotated method.
You will need to retrieve the bean manually from the applicationContext using getBean() method.
#Value("${awesomeController}")
private String myControllerName;
#PostConstruct
public void init(){
AnotherController myController = (AnotherController) appContext.getBean(myControllerName);
}
I'm not sure if what you're doing is possible but I can suggest a slightly different approach, but only if you're using Spring 3.1+. You could try using Spring Profiles.
Define the different controllers you want, one per profile:
<beans>
<!-- Common bean definitions etc... -->
<beans profile="a">
<bean id="anotherController" class="...AnotherController" />
</beans>
<beans profile="b">
<!-- Some other class/config here... -->
<bean id="anotherController" class="...AnotherController"/>
</beans>
</beans>
Your Controller would lose the #Qualifier and become something like:
#Autowired
public MyController(AnotherController anotherController) {
...
}
Then at runtime you can specify which controller bean you want to use by activating the corresponding profile using a system property, e.g.:
-Dspring.profiles.active="a"
or:
-Dspring.profiles.active="b"
It may be possible to set profiles based on a property file but you can find out more about Spring Profiles from this post on the Spring blog. I hope that helps somewhat.
I am having difficulty understanding why something in Spring Java Config using #Autowired does not work.
First, I am trying to move all my #Autowired annotations in the Java Config classes. This has the effect of making my "POJOs" back into real POJOs. I can then not only test them easily outside of a Spring context, but can also use mock objects easily and readily.
So I first tried this:
#Configuration
public class Module3ConfigClass {
#Autowired
private Module1Bean1 module1Bean1;
#Autowired
private Module2Bean1 module2Bean1;
#Bean
public Module3Bean1 module3Bean1() {
return new Module3Bean1(module1Bean1, module2Bean1);
}
}
However, when the Module3Bean1 constructor is invoked, both passed in Beans are null. If you didn't follow my made up naming convention above, both of those beans would be created by a separate Java Config configuration file. Also note that everything is wired up correctly - I know this because everything works perfectly when the #Autowired tags are on the corresponding private member fields inside of Module3Bean1.
FWIW, I tried adding an #DependsOn annotation to module3Bean1() method, but had the same results. I guess I just would really like to understand this behavior, is it correct (I suspect it is, but why)?
Finally, I found an acceptable workaround shown here:
#Configuration
public class Module3ConfigClass {
#Bean
#Autowired
public Module3Bean1 module3Bean1(Module1Bean1 module1Bean1, Module2Bean1 module2Bean1) {
return new Module3Bean1(module1Bean1, module2Bean1);
}
}
This seems fine to me, but if anyone would care to comment on it, that would be welcome as well.
I think you came across same problem I just had. In my case problem was invalid xml configuration. In my module B I had config like :
<beans>
<context:component-scan base-package="com.moduleB"/>
<import resource="classpath:applicationContext-moduleA.xml"/>
</beans>
In moduleA context I placed "context:annotation-config" annotation.
When I change import/context order to :
<beans>
<import resource="classpath:applicationContext-moduleA.xml"/>
<context:component-scan base-package="com.moduleB"/>
</beans>
Autowiring for configuration class properties started to work.
We had the same issue and came to the conclusion that the error arose because we had a circular dependency where a BeanPostProcessor was involved.
A PropertyPlaceholderConfigurer (a BeanPostProcessor) has been configured to set its propertiesArray property with the help of another bean:
<bean id="globalPropertyPlaceholderConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
lazy-init="false" depends-on="javaLoggingConfigurer">
<property name="locations">
<list>
<value>classpath:config/host/${env.instance}.properties</value>
<value>WEB-INF/config/host/${env.instance}.properties</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="true" />
<property name="propertiesArray" value="#{springPropertyFinder.findProperties()}" />
</bean>
The used springPropertyFinder bean to set the propertiesArray is not a BeanPostProcessor but a "normal" bean that gathers all Properties instances with:
public Properties[] findProperties() {
Map<String, Properties> propertiesMap = applicationContext.getBeansOfType(Properties.class);
for (String title : propertiesMap.keySet()) {
PropertiesLoggerUtil.logPropertiesContent(logger, "Springcontext Properties ("+title+")", propertiesMap.get(title));
}
return propertiesMap.values().toArray(new Properties[propertiesMap.size()]);
}
The #Configuration class contained a bean of type Properties
So our assumption is that the #Configuration class has been created without being processed by the ConfigurationClassPostProcessor (also a BeanPostProcessor), because the PropertyPlaceholderConfigurer depends on the springPropertyFinder, which depends on the properties bean in the #Configuration class. The order of the BeanPostProcessors is probably not setup right under these circumstances.
This described setup worked in XML, but not with Java config.
I have a bean that i want to inject with a named list using Spring util namespace <util:list id="myList"> but Spring is looking for a collection of beans of type String instead. My broken test is:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class ListInjectionTest {
#Autowired #Qualifier("myList") private List<String> stringList;
#Test public void testNotNull() {
TestCase.assertNotNull("stringList not null", stringList);
}
}
My context is:
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<util:list id="myList">
<value>foo</value>
<value>bar</value>
</util:list>
</beans>
But I get
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [java.lang.String] found for dependency [collection of java.lang.String]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true), #org.springframework.beans.factory.annotation.Qualifier(value=myList)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:726)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:571)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:412)
Which puzzles me rather as I figured this would be the way it was expected to work.
This is due to a rather obscure part of #Autowired's behaviour, specified in 3.11.2. #Autowired:
It is also possible to provide all
beans of a particular type from the
ApplicationContext by adding the
annotation to a field or method that
expects an array of that type...
The same applies for typed collections...
In other words, by saying #Autowired #Qualifier("myList") List<String>, you're actually asking for "give me the list of all beans of type java.lang.String that have the qualifier "myList".
The solution is mentioned in 3.11.3. Fine-tuning annotation-based autowiring with qualifiers:
If you intend to express
annotation-driven injection by name,
do not primarily use #Autowired - even
if is technically capable of referring
to a bean name through #Qualifier
values. Instead, prefer the JSR-250
#Resource annotation which is
semantically defined to identify a
specific target component by its
unique name, with the declared type
being irrelevant for the matching
process.
As a specific consequence of this
semantic difference, beans which are
themselves defined as a collection or
map type cannot be injected via
#Autowired since type matching is not
properly applicable to them. Use
#Resource for such beans, referring to
the specific collection/map bean by
unique name.
So use this in your test, and it works fine:
#Resource(name="myList") private List<String> stringList;
Another thing that could be happening is that you are autowiring a property of a bean. In such case you dont need to autowire it, but just create the setter method and use the property tag in the bean definition (when using xml) example:
<bean id="cleaningUpOldFilesTasklet" class="com.example.mypackage.batch.tasklets.CleanUpOldFilesTasklet">
<property name="directoriesToClean">
<list>
<value>asfs</value>
<value>fvdvd</value>
<value>sdfsfcc</value>
<value>eeerer</value>
<value>rerrer</value>
</list>
</property>
</bean>
And the class:
public class CleanUpOldFilesTasklet extends TransferingFilesTasklet implements Tasklet{
private long pastMillisForExpiration;
private final String dateFormat = "MM.dd";
Date currentDate = null;
List<String> directoriesToClean;
public void setDirectoriesToClean(List<String> directories){
List<String> dirs = new ArrayList<>();
for(String directory : directories){
dirs.add(getSanitizedDir(directory));
}
this.directoriesToClean = dirs;
}
See, no #Autowired annotation in class.