How does Spring inject list as constructor argument - java

I've got this:
#Named
#Singleton
public class MyDefaultDef {
#Inject
public MyDefaultDef(SomeRef someRef, List<AnotherRef> anotherRefs) {
//...
}
//...
}
Question1: [main] How does work autowiring of List<AnotherRef> anotherRefs as a constructor arg?
I mean if I'd like to replace that bean definition with xml, I had to specify each element of the list. I.e.
<constructor-arg>
<list>
<ref bean="..."/>
<ref bean="..."/>
</list>
</constructor-arg>
But from where Spring takes those elements in case of annotations?
Question2: How to replace MyDefaultDef bean definition to xml?

When Spring needs to autowire a List<SomeBean>, it looks up all beans in its BeanFactory and retrieves all those that are of type SomeBean. It thens creates a List and adds them to it. It then autowires it.
There is no way in Spring XML to do this listing by type. You'll need to compromise. You leave your class as such
public class MyDefaultDef {
#Inject
public MyDefaultDef(SomeRef someRef, List<AnotherRef> anotherRefs) {
//...
}
//...
}
and simply declare
<bean class="com.example.MyDefaultDef" autowire="constructor"/>
Spring will end up using your constructor above.

Related

Bean initialized with FactoryBean receives the FactoryBean instead of the Obect created by it

I'm trying to reference a bean from another with xml, sending an object produced with a FactoryBean. My problem is that the data generated by the beanFactory is, apparently a FactoryBean object and hasn't the type that should have been created by the factory.
The beans are defined like this:
<bean id="daFactoryBean" class="com.whatever.something.MyFactoryBean">
[...]
</bean>
<bean id="theBeanThatProducesProblems" class="com.whatever.something.AGoodName">
<constructor-arg ref="daFactoryBean"/>
<constructor-arg ref="anotherBean"/>
</bean>
Note that "daFactoryBean" would produce objects typed "TheClassIWantToProduce". And that "theBeanThatProducesProblems" expects that type on "daFactoryBean" but it receives the Factory itself.
The factoryBean is defined like this
public class MyFactoryBean implements
FactoryBean<TheClassIWantToProduce> {
FactoryBean([..]) { [...] }
[...]
#Override
public TheClassIWantToProduce getObject() { [...] }
}
and the class that I need to produce is this
public AGoodName extends RestTemplate {
AGoodName(TheClassIWantToProduce foo, AnotherClassThatDoesnTComeFromAFactory foo2){
}
}
The thing is that I don't exactly know how to say the spring's xml that I want the object produced by the factory, not the factory itself. As the types aren't valid, the expected value isn't being initialised. Any hints?
Solved, I had to create a sub-bean that used my factory-bean
<bean id="daFactoryBean" class="com.whatever.something.MyFactoryBean">
[...]
</bean>
<bean id="theBeanThatProducesProblems" class="com.whatever.something.AGoodName">
<constructor-arg>
<bean id="aNewBean" factory-bean="daFactoryBean" class="com.whatever.something.TheClassIWantToProduce"/>
</constructor-arg>
<constructor-arg ref="anotherBean"/>
</bean>

Use abstract Spring bean as constructor-arg

We have a hierarchy of handler classes in our codebase which implement a kind of chain-of-responsibility principle. There is an abstract parent class and it is extended by several child classes, which also receive the abstract in their constructor
public abstract class AbstractHandler {
public AbstractHandler(final AbstractHandler next, final PropertyName propertyName) {
this.next = next;
this.propertyName = propertyName;
}
...
public class OneConcreteChildHandler extends AbstractHandler {
public OneConcreteChildHandler(final AbstractHandler next) {
super(next, PropertyName.OneConcreteChild);
}
...
We would now need to inject an instance of one of the concrete child classes into a newly implemented service class, and we should configure this in XML. We can configure an abstract bean for the abstract parent class, but this one then does not seem to be allowed to be used as constructor-arg for the concrete child bean
<bean id="abstractHandler" abstract="true" class="...AbstractHandler" />
<bean id="oneConcreteChildHandler" class="...OneConcreteChildHandler" parent="abstractHandler">
<constructor-arg ref="abstractHandler"/> //"abstract bean can not be used here"
</bean>
<bean id="someService" class="...SomeService">
<constructor-arg ref="oneConcreteChildHandler"/>
...
Is there any way to overcome this? The handler class hierarchy is legacy code and we are not able to modify their sources at this point.
The main problem here is that you are trying to inject a abstract bean. You should not do this. This abstractHandler should be used just for mapping the parent in child bean. Despite, it doesnt seen to be really what you want/need. You wouldn't pass a abstract object in this constructor, but another child class's object. You Chain must have a end point, where the constructor's argument next will be null like that:
<bean id="abstractHandler" abstract="true" class="...AbstractHandler" />
<bean id="oneConcreteChildHandler" class="...OneConcreteChildHandler" parent="abstractHandler">
<constructor-arg ref="twoConcreteChildHandler"/>
</bean>
<bean id="twoConcreteChildHandler" class=".." parent="abstractHandler">
<constructor-arg name="next">
<null />
</constructor-arg>
</bean>
<bean id="someService" class="...SomeService">
<constructor-arg ref="oneConcreteChildHandler"/>
...
Spring's abstract bean concept is NOT the same as Java's abstract class concept.
In Java, you cannot instantiate an instance of an abstract class, and therefore trying to map an abstract bean to an abstract java type will not work when it comes time to instantiate it.
I would recommend that you instead look at how Servlet Filters work (via the FilterChain), or Spring's HandlerInterceptor pattern.

How can I inject an instance of List in Spring?

What works
Suppose I have a spring bean definition of an ArrayList:
<bean id="availableLanguages" class="java.util.ArrayList">
<constructor-arg>
<bean class="java.util.Arrays" factory-method="asList">
<constructor-arg>
<list>
<value>de</value>
<value>en</value>
</list>
</constructor-arg>
</bean>
</constructor-arg>
</bean>
Now I can inject this into all kinds of beans, e.g. like this:
#Controller
class Controller {
#Autowired
public Controller(ArrayList<String> availableLanguages) {
// ...
}
}
This works great.
How it breaks
However if I change my controller a tiny bit and use the type List instead of ArrayList like this:
#Controller
class Controller {
#Autowired
public Controller(List<String> availableLanguages) {
// ...
}
}
Then instead I get a list of all beans of type String rather then the bean I defined. However I actually want to wrap my List into an unmodifiable List, but this will only be possible if I downgrade my dependency to a list.
So far discovered workaround
The following XML file:
<bean id="availableLanguages" class="java.util.Collections" factory-method="unmodifiableList">
<constructor-arg>
<bean class="java.util.Arrays" factory-method="asList">
<constructor-arg>
<list>
<value>de</value>
<value>en</value>
</list>
</constructor-arg>
</bean>
</constructor-arg>
</bean>
works together with this controller:
#Controller
class Controller {
#Autowired
public Controller(Object availableLanguages) {
List<String> theList = (List<String>)availableLanguages;
}
}
While this works the extra type cast is ugly.
Findings so far
I figured that there is a special handling for collections in Spring 4.2.5 (the currently most recent version) which seems to cause all the trouble. It creates special behaviour when a parameter is an interface that extends Collection. Thus I can workaround by using Object or a concrete implementation as parameter type.
Question
Is there any way to directly inject a list into a bean? How?
Using #Qualifier will inject the bean with the given qualifier. You can name the list which you want to be a bean and that will work fine.

Adding all the instances in a package to a List with Spring

I have a class "Box" with add method accepting all the fruits:
public class Box {
List <IFruit> fruits;
public void add (IFruit fruit) {
fruits.add(fruit);
}
}
I would like to define with Spring's applicationContext.xml a singleton instance of this class, which would have all the IFruits implementations added (those appear in a package x.y.fruits, for inst. x.y.fruits.Apple).
The first part is easy:
<bean id="box" class="x.y.Box"/>
But how to wire all the IFruit instances?
Thanks!
If you #Autowire the field, you do not need to define anything, Spring will find all instances of the IFruit interface in the application context and load them in.
public class Box {
#Autowired
List <IFruit> fruits; //This should contain all IFruit's in the ApplicationContext
public void add (IFruit fruit) {
fruits.add(fruit);
}
}
Of course, you need to add the element <context:annotation-config/> to your xml configuration for #Autowired to work...
If you create a setter for the list, say setFruits, you can wire it like this:
<bean id="box" class="x.y.Box">
<property name="fruits">
<list>
<ref bean="fruit1" />
<ref bean="fruit2" />
...
</list>
</property>
</bean>
<bean id="fruit1" class="x.y.fruits.Apple" />
...
You can also do this similarly using constructor injection.

Optional Spring bean references

In my application I am using ContextLoaderListener to load context files from many jars using:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:META-INF/contextBeans.xml</param-value>
</context-param>
This means I can reference beans from other jars without doing import.
In the application there are multiple deployment options and in some deployments jars can be excluded. To support that I would like some bean references to be optional. For example:
<bean id="mainAppBean" class="com.someapp.MyApplication">
<constructor-arg index="0" ref="localBean"/>
<constructor-arg index="1" ref="optionalBeanReference1"/>
<constructor-arg index="2" ref="optionalBeanReference2"/>
</bean>
In the example above I would like to have optionalBeanReference1 equal null if the reference was not found (mark it optional in some way)
Can this be done in Spring? or what method do you recommend for handling dynamic references?
My best guess is to use autowire-ing with required false. Don't know how you can express this in XML but using annotation configuration this would look like:
#Autowired(required=false)
With recent versions of Spring (tested with spring 4.1) and Java Configuration and Java 8, you can use Optional in parameters, and are only autowired if available.
#Autowired
public MyApplication(Optional<YourOptionalObject> maybeObject) {
// do something with the optional autowired
}
what method do you recommend for handling dynamic references?
I think #cristian's #Autowired answer is a good one. That will call the setter methods if the beans of that type are available. However, if you have multiple beans of the same type, I believe Spring throws an exception. If you cannot use #Autowired for this or some other reason, I see a couple of solutions:
You could make your class ApplicationContextAware and lookup the beans in the context yourself:
public void setApplicationContext(ApplicationContext applicationContext) {
if (applicationContext.containsBean("optionalBeanReference1")) {
setOptionalBeanReference1(
(OptionalBeanReference1)applicationContext.bean(
"optionalBeanReference1");
}
...
}
You could invert the dependency. Each of the optional classes could set themselves on the mainAppBean. I use this in certain situations when a direct dependency would cause loops or other problems.
<bean id="optionalBeanReference1" class="com.someapp.SomeClass">
<constructor-arg index="0" ref="mainAppBean"/>
</bean>
Then in the SomeClass:
public SomeClass(com.someapp.MyApplication mainAppBean) {
mainAppBean.setOptionalBeanReference1(this);
}
You could stay with your direct dependency and then either import a file with the beans defined or import another file where you define the beans as having null values by using a factory bean. See this factory code.
Good luck.
There's no built-in mechanism for this. However, you could write a pretty trivial FactoryBean implementation to do this for you, something like this:
public class OptionalFactoryBean extends AbstractFactoryBean<Object> implements BeanNameAware {
private String beanName;
#Override
public void setBeanName(String beanName) {
this.beanName = BeanFactoryUtils.originalBeanName(beanName);
}
#Override
protected Object createInstance() throws Exception {
if (getBeanFactory().containsBean(beanName)) {
return getBeanFactory().getBean(beanName);
} else {
return null;
}
}
#Override
public Class<?> getObjectType() {
return null;
}
}
You can then use it like this:
<bean id="mainAppBean" class="com.someapp.MyApplication">
<constructor-arg index="0" ref="localBean"/>
<constructor-arg index="1">
<bean name="optionalBeanReference1" class="com.someapp.OptionalBeanFactory"/>
</constructor-arg>
<constructor-arg index="2">
<bean name="optionalBeanReference2" class="com.someapp.OptionalBeanFactory"/>
</constructor-arg>
</bean>
Given that the bean references in your XML config are defined via expression language (EL) you can do the following:
<property name="cache" value="#{getObject('optionalCache')}" />
which makes use of the BeanExpressionContext.getObject() method. See here for more details.

Categories