tags:

views:

5092

answers:

2

I have the following XML configuration:

<bean id="bean1" class="Simple"/>
<bean id="bean2" class="Simple"/>

<bean id="tasks" class="java.util.ArrayList">
    <constructor-arg>
        <list>
            <ref bean="bean1" />
            <ref bean="bean2" />                
        </list>
    </constructor-arg>
</bean>

<bean id="list" class="Comp">
    <property name="tasks" ref="tasks"/>
</bean>

The "tasks" contains all beans of type Simple. The problem with this is that I might forget to add a Simple bean I've configured to the list.

I could do this programatically using

Map map = context.getBeansOfType(Simple.class);

and setting the list bean with the beans retrieved.

Is there any way of doing this using just XML configuration?

+6  A: 

Your context file should like this:

<bean id="bean1" class="Simple"/>
<bean id="bean2" class="Simple"/>

<bean id="list" class="Comp" autowire="byType"/>

Note the autowire="byType" addition, and the autowiring documentation.

Robert Munteanu
Wouldn't this just put the list itself in the Comp's tasks property? How does this help populating the list itself with all beans of type Simple?
rudolfson
@rudolfson: the list should not exist anymore, and the javadoc for @Autowired has a nice explanation of the rules: http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/beans/factory/annotation/Autowired.html
Robert Munteanu
Thanks for enlightening me. I didn't read the autowire docs to the end and so wasn't aware of this capability. +1 for you answer!
rudolfson
+1  A: 

I would suggest writing your own FactoryBean for creating such a list. This would be reusable and then configurable using XML only. The FactoryBean would look like

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.Assert;

public class CollectingListFactoryBean implements FactoryBean, ApplicationContextAware {
    private ApplicationContext appCtx;
    private Class type;

    public Object getObject() {
        Assert.notNull(type, "type must be initialized");
        List result = new ArrayList();
        result.addAll(appCtx.getBeansOfType(type).values());
        return result;
    }

    public Class getObjectType() {
        return List.class;
    }

    public boolean isSingleton() {
        return false;
    }

    public void setApplicationContext(ApplicationContext applicationContext) {
        this.appCtx = applicationContext;
    }

    public void setType(Class type) {
        this.type = type;
    }
}

Then your XML configuration would be

<bean id="bean1" class="Simple"/>
<bean id="bean2" class="Simple"/>

<bean id="tasks" class="CollectingListFactoryBean">
    <property name="type" value="Simple" />
</bean>

<bean id="list" class="Comp">
    <property name="tasks" ref="tasks"/>
</bean>

NOTE: I didn't have the time to test the example above. I just used code I already had as a template. ;) Especially I'm not sure if passing Simple as a Class argument for the type property works this way. Just give it a try. In the worst case you would have to use String as the property type and use Class.forName(type) to get your class. But my guess is that Spring does this transformation for you.

EDIT Even though this solution should work, I recommend Robert Munteanu's answer.

rudolfson