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.
Related
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>
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.
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.
I have a list of Class types that I need to iterate over and for every type in that list, Spring needs to automatically find the correct sort of object to initialize.
For example : I have a ClassA and ClassB. When for example there is a type User, a ClassA needs to be initialized with certain parameters (which are defined in the context.xml). When for example there is a type of Message, it should also make an object from type ClassA but with different parameters, which are again defined in the context.xml.
How would I go about doing this? I have looked at the inversion of control feature of spring but cannot seem to find the correct implementation for my specific purpose
One idea was that I make a bean for every kind of possible type such as
<bean id="classAconfig" class="ClassA">
<property name="type" value="User.class"/>
<property name="fields">
<list>
<value>id</value><value>email</value>
</list>
</property>
<property name="map">
<map>
<entry key="id" value="1"/><entry key="login" value="1"/><entry key="email" value="1"/>
</map>
</property>
</bean>
Kind regards,
Merlijn
Well, I found it. Just had to define some beans in the context xml like described above. But in ClassA I extended InitializingBean and overrided the afterPropertiesSet methode. In that methode I registered the ClassA in my service.
public class ClassA extends InitializingBean
#Override
public void afterPropertiesSet() throws Exception {
service.registerClass(this);
}
And then in my service :
public void registerClass(#Nonnull Class clazz) {
classes.put(clazz.getType(), clazz);
}
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.