I was wondering if it is possible to specify x amount of the same bean in a list in Spring. For example, instead of having beans with ids: stage1, stage2,... stageN, as here:
<bean id="stage1" class="Stageclass"/>
<bean id="stage2" class="Stageclass"/>
<bean id="stages" class="java.util.ArrayList">
<constructor-arg>
<list>
<ref bean="stage1" />
<ref bean="stage2" />
</list>
</constructor-arg>
</bean>
Would it be possible to do something like the following?:
<bean id="stage1" class="Stageclass"/>
<bean id="stages" class="java.util.ArrayList">
<constructor-arg>
<list>
<ref bean="stage1" duplicate="20 times"/>
</list>
</constructor-arg>
</bean>
Thanks in advance.
If you use annotation based configuration and you specified list of objects with same interface as dependency for some class then spring will auto-wire aut-wire then for free. Example:
interface StageInterface {
//...
}
class StageImpl1 implements StageInterface {
//...
}
class StageImpl2 implements StageInterface {
//...
}
#Component
class StageContainer {
private final List<StageInterface> stages;
#Autowired
public StageContainer(List<StageInterface> stages) {
this.stages = stages;
}
public List<StageInterface> getStages() {
return stages;
}
}
This is a spring version 3+ feature.
I believe the same is possible with xml configuration as well. In your case that's probably will be the same class(StageClass), but with different configuration parameters.
Lookup method injection from http://static.springsource.org/spring/docs/2.5.x/reference/beans.html solved the problem. Just needed to make sure the bean I wanted multiple instances of had scope="prototype"
You can't do that using standard Spring's default namespace. However you can implement your own custom namespace where you could support such syntax.
Alternatively, you can implement a static method that would create an ArrayList instance with duplicated elements.
Related
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.
Java
public class MyObject{}
public class MyFactory{
private Optional<MyObject> myproperty;
public Optional<MyObject> getMyproperty{...}
public void setMyproperty{...}
}
Spring config xml (doesn't work)
<bean id="myproperty" class="java.util.Optional">
<constructor-arg>
<value>com.MyObject</value>
</constructor-arg>
</bean>
<bean id="myfactory" class="com.Myfactory">
<property name="myproperty" ref="myproperty" />
</bean>
Does spring support generics beans?
The reason for using Optional is it provide some useful features such as checking value if null. You can complete checking and further action in one line of code.
getMyproperty().ifPresent(id -> call.setId(id));
Seems the problem have nothing to do with generics.
You simply need to properly tell Spring to create the bean using a factory method, as Optional can only be created though factory methods. Something like:
<bean id="myproperty" class="java.util.Optional" factory-method="of">
<constructor-arg type="java.lang.Object" value="com.MyObject" />
</bean>
for which it is supposed to mean creating the myproperty bean by Optional.of(com.MyObject.class) (Change the factory-method to ofNullable if that's the one you want to use)
Another option is to use SpEL (Spring Expression Language):
<bean id="mybean" ...>
<property name="optProp" value="#{ T(java.util.Optional).of( #wrapme) }"/>
</bean>
Where "wrapme" is the name of a bean defined elsewhere that you want to wrap in java.util.Optional.
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.
Let's say we have two beans, defined in Spring
<bean class="foo.A"/>
<bean class="foo.B"/>
public class A {
#Autowired
private B b;
}
public class B {
public void foo() {
...
}
}
What I want to achieve is the interception of all calls to B.foo(). Looking at documentation, I wrote interceptor C and changed the definition of bean B as follows:
public class C implements org.springframework.aop.MethodBeforeAdvice {
public void before(final Method method, final Object[] args, final Object target) {
// interception logic goes here
}
}
<bean class="foo.C"/>
<bean class="org.springframework.aop.framework.ProxyFactoryBean" scope="prototype">
<property name="proxyTargetClass" value="true"/>
<property name="singleton" value="false"/>
<property name="target">
<bean class="foo.B" scope="prototype"/>
</property>
<property name="interceptorNames">
<list>
<value>foo.C</value>
</list>
</property>
</bean>
Problem: when starting up, Spring container complains: No matching bean of type [foo.B] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. In other words, it can't inject B into A because B is hidden behind org.springframework.aop.framework.ProxyFactoryBean and no longer "automagically" recognized. If I replace the definition into a simple class=foo.B, container starts fine. What's the best way to solve this?
Bonus question: is it possible to implement interception of B.foo() without involvement of ProxyFactoryBean and only using annotations (preferably without involvement of <aop:...)?
Define an interface for foo.B (e.g. foo.BInterface) and use foo.BInterface in the class A.
Also pay attention that Autowired injections are done only once. So if foo.A is singleton, it will receive only the first created instance of foo.B, while you want it to be a prototype.
Answer to bonus: Yes, but it may be more complicated. As a possible solution you can implement BeanPostProcessor. In the implementation you can replace the foo.B with dynamic proxy. So basically you do the same, but instead of using <aop: you do it yourself using basic Spring functionality. And again: you don't solve the "prototype is not autowired" problem and you still need an interface.
Perhaps you want to have the Bean 'foo.B' defined outside of the ProxyFactoryBean, and refer to it from the target property.
<bean class="foo.C"/>
<bean id="fooB" class="foo.B" scope="prototype"/>
<bean class="org.springframework.aop.framework.ProxyFactoryBean" scope="prototype">
<property name="proxyTargetClass" value="true"/>
<property name="singleton" value="false"/>
<property name="target" ref="fooB"/>
<property name="interceptorNames">
<list>
<value>foo.C</value>
</list>
</property>
</bean>
Tarlog's answer is correct, but to make it more clear:
you should wire objects by their interface, not by their class:
public class A {
#Autowired
private C b;
}
public class B implements C{
public void foo() {
...
}
}
I'm getting reacquainted with Spring and looking at dependency injection and IoC in a way I haven't before.
If I want to build a string, say for a file name, and I already have a Spring bean which contains the directory, what is the best way to append the file name?
Writing a bean to do this myself seems fairly trivial, but I would think that Spring might already have the capability to do this somewhere though its API. If this is possible, how?
Just for kicks, here is the implementation of the fairly simple bean....
public class MySimpleStringAppender {
private final StringBuffer myString = new StringBuffer();
public MySimpleStringAppender(List<String> myStrings) {
for (String string : myStrings) {
myString.append(string);
}
}
public String getMySimpleString() {
return myString.toString();
}
}
and configured with...
<bean id="filename" class="MySimpleStringAppender">
<constructor-arg ref="filenameStrings"/>
</bean>
<util:list id="filenameStrings">
<ref bean="directory"/>
<value>filename.txt</value>
</util:list>
<bean id="directory" class="java.lang.String">
<constructor-arg value="C:/myDirectory/"/>
</bean>
So while it's not a lot of work or code, I'd think there would be something available so I wouldn't need to write this at all.
Nope never seen such a thing. You can also make your XML simpler by combining all that into one:
<bean id="filename" class="MySimpleStringAppender">
<constructor-arg>
<list>
<value>C:/myDirectory</value>
<value>filename.txt</value>
</list>
</constructor-arg>
</bean>
But you probably already knew that.
Maybe define "c:/myDirectory" as a property and do:
<bean id="filename" class="java.lang.String">
<constructor-arg value="${dir}/filename.txt"/>
</bean>
Will it work?