Search code examples
javaspringdependency-injectionconstructor-injectionspring-ioc

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.


Solution

  • 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"/>
        ...