With constructor autowiring Spring does not throw an exception when there are multiple implementation types - spring

There are interfaces Work and You. There are implementation types WorkImpl and YouImpl and YouImpl2.
I use constructor autowire to inject a You implementation instance in a WorkImpl instance.
Because there are multiple You implementations types, I thought Spring would throw an exception. But Spring instantiates an instance of one of the implementation types, in my case it was YouImpl. This is what I do not understand.
The configuration file is partly,
<bean
id="work"
class="my.test.own.spring_book.WorkImpl"
autowire="constructor"
>
<property name="age" value="52"/>
<property" name="name" value="Foo Bar"></property>
</bean>
<bean
id="you"
class="my.test.own.spring_book.YouImpl"
>
</bean>
<bean
id="you2"
class="my.test.own.spring_book.YouImpl2"
>
</bean>
WorkImpl has one constructor,
public WorkImpl(You you) {
this.you=you;
}

There are few types of autowiring using configuration approach:
byName
byType
constructor
autodetect:- Similar to byType, but type applies to constructor arguments.
Spring container looks at the constructor of the beans on which autowire attribute is set to byType in the XML configuration file. It then tries to match and wire a property if its type matches with exactly one of the beans name in configuration file.
<bean id="you" class="my.test.own.spring_book.YouImpl">
</bean>
<bean id="you2" class="my.test.own.spring_book.YouImpl2">
</bean>
It will match with you as name of parameter used in constructor is you
public WorkImpl(You you) {
this.you=you;
}
In order to avoid this you can use autowire-candidate="false" hence that bean will not take part in autowiring
<!-- This bean will not be injected-->
<bean id="you" class="my.test.own.spring_book.YouImpl" autowiring-candidate="false">
</bean>
<bean id="you2" class="my.test.own.spring_book.YouImpl2">
</bean>
Above is the answer of your question. But I will try to explain more so I can use this answer for future if I forget.
Now suppose you don't give id attribute to the bean or value of id attribute is different than the constructor parameter name.
<bean id="you1" class="my.test.own.spring_book.YouImpl" autowiring-candidate="false">
</bean>
<bean id="you2" class="my.test.own.spring_book.YouImpl2">
</bean>
Spring container searches any bean with type You, yes found two. Do next step
Spring container sees any bean with name(i.e id="you") you. No
It throws exception Unsatisfied dependency Injection
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'work' defined in class path resource [autowire-contructor.xml]: Unsatisfied dependency expressed through constructor argument with index 0 of type
[my.test.own.spring_book.You]: : No unique bean of type [my.test.own.spring_book.You] is defined: expected single matching bean but found 2: [you1, you2]; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException

Related

Unable to Autowire a bean of type String

I have a bean as defined below which I want to autowire in to a Class which is defined as a bean in the Spring context file. But its not working, Strangely the other object bean types autowired in the same class are being autowired correctly.
Bean to Autowire is as below :-
<bean id="stringToAutowire" class="java.lang.String">
<constructor-arg value="true" />
</bean>
Class where its to be Autowired is :- I have tried annotating it with #Component .But no success.
public class AService {
#Autowired
private BDao bDao;
#Autowired
private String stringToAutowire;
........
}
context file is as :-
<context:annotation-config/>
<context:component-scan base-package ="PKG "/>
<bean id="aService" class="AService"/>
<bean id="bDao" class="BDao"/>
<bean id="stringToAutowire" class="java.lang.String">
<constructor-arg value="true" />
</bean>
In the Spring documentation:
http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-autowired-exceptions
There is this text "You cannot autowire so-called simple properties such as primitives, Strings, and Classes (and arrays of such simple properties). This limitation is by-design."
I have not found an exact specification of what happens in this case. In my experience Autowire of String properties is unreliable. Sometimes works, sometimes not. So I recommend avoiding autowire of string values.
In your case you are using both Autowire and constructor-arg. They are separate mechanisms. Only one is required.
Ditch Autowire for String.
Add a constructor to AService that takes, as the first argument, a string to assign to "stringToAutowire". The "constructor-arg" will specify what to pass for this constructor argument.
try using below:
#Autowired
#Qualifier("stringToAutowire")
private String someString;
You can not autowire simple properties such as primitives, Strings, and Classes (and arrays of such simple properties) and explicit dependencies in property and constructor-arg settings always override autowiring.
So drop #Autowired annotation from stringToAutowire and use with property.

How do I inject a Class[] array in spring?

I've got a class which has a static method newInstance(Class[] classes, List properties), and I need Spring to be able to instantiate instances using this class.
What i've got so far in my XML configuration is :
<bean id="jaxbContext" scope="prototype" class="javax.xml.bind.JAXBContext" factory-method="newInstance">
<constructor-arg>
<list>
<value type=">com.company.pas.entity.Partner.class</value>
</list>
</constructor-arg>
<constructor-arg>
<list>
<value>com/company/pas/entity/mapping/partner-pojo2xml.xml</value>
</list>
</constructor-arg>
</bean>
This throws the below exception. Note that it complains that it can't find the newInstance method, but i assume that is because it can't find a newInstance method with a signature which matches the configuration. I suspect that Spring interprets the list of classes as java.lang.String, and not Class.
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jaxbContext' defined in class path resource [com/company/pas/context/mappingContext.xml]: No matching factory method found: factory method 'newInstance'. Check that a method of the specified name exists and that it is static.
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:500)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:964)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:870)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:479)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:450)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:309)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:189)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:322)
... 46 more
How do I get spring to inject an array of Class[] based on a configuration file?
JAXBContext.newInstance() has this overload:
newInstance(String contextPath)
newInstance(String contextPath,ClassLoader classLoader)
newInstance(Class... classesToBeBound)
newInstance(Class[] classesToBeBound,Map<String,?> properties)
newInstance(String contextPath,ClassLoader classLoader,Map<String,?> properties)
To be honest I can't find a newInstance(Class[] classes, List properties) overload so spring is right when tells you it can't find the newInstance() method

Exported service not injecting in my bundle in Spring Dynamic Modules

I am using Spring Dynamic Modules for the first time. I have tried to expose a service (simple listofValuesDAO Bean) through a bundle and am trying to inject it in another bundle to use the bean.
Below is configuration tag in osgi-context.xml of Bundle1 through which service was exposed:
<osgi:service ref="listOfValuesDAO" auto-export="interfaces"/>
and I am trying to fetch it in Bundle2 through below tag in osgi-context.xml:
<osgi:reference id="listOfValuesDAO" interface="com.dao.IListOfValuesDAO" />
The issue is that when I try to inject it in my bean in Bundle2 using below configuration:
<bean id="exportServiceImpl" class="com.service.impl.ExportServiceImpl">
<property name="listOfValuesDAO" ref="listOfValuesDAO"/>
</bean>
System throws below exception:
Exception in thread "SpringOsgiExtenderThread-85"org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'exportServiceImpl' defined in URL [bundle://325.16:0/META-INF/spring/module-context.xml]:
Error setting property values; nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property 'listOfValuesDAO' of bean class [com.service.impl.ExportServiceImpl]:
Bean property 'listOfValuesDAO' is not writable or has an invalid setter method. Did you mean 'listOfValuesDao'?
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1396)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1118)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:517)
Below is property in my ExportServiceImpl Class:
public class ExportServiceImpl implements IExportService {
IListOfValuesDAO listOfValuesDao;
public void setListOfValuesDao(IListOfValuesDAO listOfValuesDao) {
this.listOfValuesDao = listOfValuesDao;
}
public IListOfValuesDAO getListOfValuesDao() {
return listOfValuesDao;
}
}
Could someone please help me in resolving this issue?
It seems to be a problem with case inconsistency: listOfValuesDao and listOfValuesDAO are different names.
You use the first version in the Service, and the second in the XML bean definition. Try:
<bean id="exportServiceImpl" class="com.service.impl.ExportServiceImpl">
<property name="listOfValuesDao" ref="listOfValuesDao"/>
</bean>

Is default constructor required in Spring injection?

I'm trying to inject a constructor that takes some arguments. After compiling Spring complains it couldn't find a default constructor (I haven't defined it) and throws BeanInstatiationException and NoSuchMethodException.
After defining a default constructor the exceptions don't appear anymore, however my object is never initialized with the argument constructor, only the default one is called. Does Spring really require a default constructor in this case? And if yes, how can I make it use the argument constructor instead of the default one?
This is how I wire everything:
public class Servlet {
#Autowired
private Module module;
(code that uses module...)
}
#Component
public class Module {
public Module(String arg) {}
...
}
Bean configuration:
<beans>
<bean id="module" class="com.client.Module">
<constructor-arg type="java.lang.String" index="0">
<value>Text</value>
</constructor-arg>
</bean>
...
</beans>
Stack trace:
WARNING: Could not get url for /javax/servlet/resources/j2ee_web_services_1_1.xsd
ERROR initWebApplicationContext, Context initialization failed
[tomcat:launch] org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'module' defined in URL [...]: Instantiation of bean failed;
nested exception is org.springframework.beans.BeanInstantiationException: Could not
instantiate bean class [com.client.Module]: No default constructor found; nested
exception is java.lang.NoSuchMethodException: com.client.Module.<init>()
Spring only "requires" a default constructor if you plan on instantiating it without any arguments.
for example, if your class is like this;
public class MyClass {
private String something;
public MyClass(String something) {
this.something = something;
}
public void setSomething(String something) {
this.something = something;
}
}
and you set it up in Spring like this;
<bean id="myClass" class="foo.bar.MyClass">
<property name="something" value="hello"/>
</bean>
you're going to get an error. the reason is that Spring instantiates your class new MyClass() then tries to set call setSomething(..).
so instead, the Spring xml should look like this;
<bean id="myClass" class="foo.bar.MyClass">
<constructor-arg value="hello"/>
</bean>
so have a look at your com.client.Module and see how its configured in your Spring xml
Most probably you are using component-scanning and since you define annotation #Component for class Module it tries to instantiate the bean. You do not need #Component annotation if You are using XML for bean definition.
Just faced the same problem, i guess till now you might have solved the problem.
Below is what you could have changed your bean configuration to,
<bean id="module" class="com.client.Module">
<constructor-arg value="Text"/>
</bean>

Getting an instance of a bean excplicitly in runtime

I have a case where I have a bean (let's call it A) which needs some other bean (B).
This B is retrieved from a static method of a class using MethodInvokingFactoryBean.
This static method depends on the state of the system and will work after the web application is loaded.
I need to access the B only in runtime (no interaction in the constructor).
How can I configure the A to autowire bean B and only initialize it the first time A requires it?
Is using getBean on the Application context the only way?
Thanks!
*Edit - Added some xmls :) *
This is the definition of bean B.
<bean id="api" class="com.foo.API"/>
<bean id="B" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean" lazy-init="true">
<property name="targetObject" ref="api"/>
<property name="targetMethod" value="getFactory"/>
<qualifier value="myQualifer"/>
</bean>
This is the definition of bean A.
<bean id="resources.someRESTResourceA" class="com.foo.MyRestResource"/>
I can't use Autowire to wire B into A because it will initialize it (B) on A's construction.
B's targetMethod will only work after the web app has been initialized.
I can use ApplicationContext.getBean("B") inside A, but it's not elegant and will be a problem with unit testing unless I do the following (which is also not desired):
public BInterface getB() {
if (b == null) {
b = ApplicationContext.getBean("B");
}
return b;
}
you should lazily initialize bean A.
<bean id="A" class="demo.A" lazy-init="true">
<property name="b" ref="B"/>
</bean>
You still need to retrieve the bean A from the Spring container when you need it with the getBean() method. It's easily accesible with the ApplicationContextAware interface.
If you autowire bean A into an another bean and that bean is retrieved before bean B is constructed, the Spring container will create bean A at the time it's injected as a property to the another bean.

Resources