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.
Related
I have a dao class which has a dependency on another utility class AuditStore.
package myapp;
#Repository
public class MyAppHibernateDao {
#Autowired
public void setAuditStore(AuditStore auditStore) {
ConnectorLoggingHelper.setAuditStore(auditStore);
}
}
The AuditStore.java
package myapp;
#Resource
public class AuditStore {
//too many dependencies in this class including db connection
}
Now I want to write integration test for the dao class which don't cover functionalities of 'AuditStore'.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:META-INF/spring-myapp-db-connector-test.xml")
#TestExecutionListeners({DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
public class MyAppHibernateDaoIntegrationTest {
#Test
public void test() {
//test code here
}
}
and my xml config file is
<!--spring-myapp-db-connector-test.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<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.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">
<!-- enable autowiring -->
<context:annotation-config/>
<bean id="myAppDao" class="myapp.MyAppHibernateDao">
</beans>
Whe I run this I am getting following errors.
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'myAppDao': Unsatisfied dependency expressed through method 'setAuditStore' parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'myapp.AuditStore' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
The AuditStore is a very complex object and I don't use this classes functionality for testing (I need a null or mock of this class). Is there any way I can avoid creating a bean of AuditStore defined in xml and make things work.
I know that making #Autowired(required = false) would work, that would make change in the application code for test, so I am looking for other options.
Please help if there is any alternative.
If possible you should consider moving to annotation driven configuration and use something like #InjectMock. However, assuming you are required to stick with the approach you outlined in your question, you can define a mock instance of MyAppHibernateDao in spring-myapp-db-connector-test.xml in several ways:
Use a factory bean
Use Springockito. And, of course,
Declare the myAppDao bean in spring-myapp-db-connector-test.xml as follows:
<bean id="myAppDao" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="myapp.MyAppHibernateDao"/>
</bean>
You can then #Autowire MyAppHibernateDao into MyAppHibernateDaoIntegrationTest and set expectations etc on it in your test/setup methods.
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.
I'm developing a huge application with thousands of Spring beans registered using annotations and wired with each others with #Autowired
The application will be released as a "core" application and our customers should be able to customize it adding or overwriting beans.
If they hace to modify a bean the regular way would be by extending the class and making the Spring context to register the customized class instead of the "core" one, but doing that Spring throws an error because it finds two implementation for the same interface.
How can I achieve that?
How can our customers "de-register" the core class and regster the customizaed one?
Thanks a lot!
You can use a Qualifier. They identify beans by name, not by type.
You also can set the field to a list of beans.
#Autowire
private Foobar[] customizedAndCore;
The simplest way of handling that is by using the #Primary annotation. Spring will inject the primary instead of failing with the duplicate bean exception.
Here a basic draft to use #Qualifier plus #Autowired annotation
Here is the content of Student.java file:
public class Student {
private Integer age;
private String name;
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Here is the content of Profile.java file:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
public class Profile {
#Autowired
#Qualifier("student1")
//#Qualifier("student2")
private Student student;
public Profile(){
System.out.println("Inside Profile constructor." );
}
public void printAge() {
System.out.println("Age : " + student.getAge() );
}
public void printName() {
System.out.println("Name : " + student.getName() );
}
}
Following is the content of the MainApp.java file:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("Beans.xml");
Profile profile = (Profile) context.getBean("profile");
profile.printAge();
profile.printName();
}
}
Consider the example of following configuration file Beans.xml:
<?xml version="1.0" encoding="UTF-8"?>
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<!-- Definition for profile bean -->
<bean id="profile" class="com.Profile">
</bean>
<!-- Definition for student1 bean -->
<bean id="student1" class="com.Student">
<property name="name" value="Zara" />
<property name="age" value="11"/>
</bean>
<!-- Definition for student2 bean -->
<bean id="student2" class="com.Student">
<property name="name" value="Nuha" />
<property name="age" value="2"/>
</bean>
</beans>
The way I see it you have 3 general approaches...
Create a child context and in the child context override beans in the parent context (they will replace them)
Use a profile
Use a customisation of the annotation when injecting the bean
The first option means that customers when they wish to replace the "core" beans with their own ones simply need to create a new factory which is a child of the core factory. As long as the bean in the child has the same name as the on in core all is well.
The second one means the default bean will be in the "default" (no profile) and then the replacement will be in a specific profile. These replacement beans will then override the beans in no profile when that profile is active. Replacement only happens against bean name not against type so when using this approach you have to ensure the new bean has the same name as the original and the injection annotation specifies the name of the bean ala
#Inject
#Named("dataSource")
private DataSource storageRepository;
The third option requires the following to appear in the annotation when using a bean
#Resource(name = "${dataSource}")
private DataSource dataSource;
Then when using this you will need a parameter called dataSource and needs to be set to the specific bean name you want to inject into that location.
e.g.
dataSource=enterpriseDataSource
then the bean named enterpriseDataSource will be injected into that location.
The way I see it approach 1 is arguably the closest fit to what you're looking for. It sounds like you have a "core" factory that you supply that customers depend on so they don't really have ownership of your source code. AFAIK approach 1 is also the only one that will allow autowire by type to work.
Approach 2 is a better fit for when you want to run in different modes... i.e. in dev, test or production mode. The reason for this is you can only override beans that are not in a profile. You can't override a bean already in a profile with a bean in another profile with this approach.
Approach 3 is in fact what I tend to use the most because it does not require profiles or factory hierarchy and allows swapping in of a different bean simply by changing a parameter's value. I wish however I did not have to keep specifying the bean name however. Something else that is possible - and something I use a lot - is swapping in a whole new config file via activation of a different profile.
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.
My XML configuration includes these bean definitions:
<bean id="abstractFormAction" class="staffing.server.action.form.AbstractFormAction" abstract="true" parent="baseAction">
<property name="volunteerSaver" ref="volunteerSaver"/>
<property name="emailSender" ref="emailSender"/>
<property name="closed" value="${form.closed}"/>
</bean>
<bean id="volunteerFormAction" class="staffing.server.action.form.VolunteerFormAction" parent="abstractFormAction">
<property name="captchaGenerator" ref="captcha"/>
</bean>
Indicating that VolunteerFormAction is a concrete implementation of AbstactFormAction, and will inherit the properties of AbstactFormAction.
In AbstractFormAction, I declare the properties like this:
#Autowired protected VolunteerSaver volunteerSaver;
#Autowired protected EmailSender emailSender;
#Autowired protected boolean closed;
I get the following exception when I try to deploy:
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'volunteerFormAction': Injection of autowired
dependencies failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Could not
autowire field: protected boolean
staffing.server.action.form.AbstractFormAction.closed; nested
exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
matching bean of type [boolean] found for dependency: expected at
least 1 bean which qualifies as autowire candidate for this
dependency. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
It seems to be complaining that it cannot find a bean of byte boolean. But why would it want a bean when have defined property 'closed' by value, not by reference?
You need to use #Value annotation for passing values using property place holders. #Autowire expects a bean of the specified type to be present in the applicationContext.
If you are autowiring the values why are you passing the values int he bean definition? I think what you need is
<bean id="abstractFormAction" class="staffing.server.action.form.AbstractFormAction" abstract="true" parent="baseAction"><bean>
<bean id="volunteerFormAction" class="staffing.server.action.form.VolunteerFormAction" parent="abstractFormAction">
<property name="captchaGenerator" ref="captcha"/>
</bean>
and
#Autowired protected VolunteerSaver volunteerSaver;
#Autowired protected EmailSender emailSender;
#Value("#{form.closed}") protected boolean closed;
If you can use component-scan you need not even specify create the beans
You can add <context:component-scan base-package="<your base package>"/> to your context.xml file and add the annotation #Controller to your controller file
You shouldn't annotate closed with #Autowired.
#Autowired instructs Spring to look up a bean of the type of the autowired field (boolean) in your context, that's why it's complaining about "No matching bean of type [boolean]"
If you inject the value from xml config, there is no need for any annotation on that field.
Based on the code you've shown, it's likely that you have a problem in the way you're loading your Spring contexts. My guess is that you're incorrectly component-scanning your controllers in both the root web application context and in the child context where the controllers are supposed to live. That means there are two instances of this class being created, and only one of them is being configured via the XML. Spring is attempting to autowire the other instance and failing with the given error. You'll find descriptions of the problem and solution in several other SO answers, like these:
Declaring Spring Bean in Parent Context vs Child Context
Spring XML file configuration hierarchy help/explanation
Spring-MVC: What are a "context" and "namespace"?
If you give more detail about your config files and context configuration, someone might be able to point out exactly where you're going wrong.