3

I have two lists generated with a FactoryBean and I would like to initialize a bean with the merged version of this two. Is there a way doing this?

<bean id="listA" class="XFactoryBean" >
  ...
</bean>
<bean id="listB" class="YFactoryBean">
  ...
</bean>
<bean >
  <property name="AwithB" ...>
</bean>

There is a solution that works with static lists (http://vikdor.blogspot.hu/2012/10/using-collection-merging-in-spring-to.html), but does not work with those generated lists.

BTakacs
  • 2,397
  • 3
  • 24
  • 26
  • Couldn't you simply inject listA and listB into the third bean, and merge both lists using Java code? – JB Nizet Jan 25 '13 at 11:17
  • Sorry to say that, but that is a nasty, specific solution... – BTakacs Jan 25 '13 at 12:00
  • possible duplicate of [Is there a way to re-use a in spring configuration file?](http://stackoverflow.com/questions/12760436/is-there-a-way-to-re-use-a-utillist-in-spring-configuration-file) – Eugene Evdokimov Jan 23 '14 at 07:16

3 Answers3

3

Java @Configuration FTW:

@Configuration
public class Config {

    @Resource
    private List listA;

    @Resource
    private List listB;

    @Bean
    public List AwithB() {
        List mergedList = new ArrayList(listA);
        listB.addAll(listB);
        return mergedList;
    }

}

Much less boilerplate.

Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
  • It seems that I need to give more attention to the annotation-based configuration: this code is much more concise and nice. Thanks – BTakacs Jan 25 '13 at 23:44
2

After checking the SpEL-related solutions (how to extend a list in spring config) and extending the ListFactoryBean (http://ericlefevre.net/wordpress/2008/04/02/merging-lists-in-a-spring-configuration-file/) I came up with the following solution:

config:

<bean id="AwithB" class="com.util.ListMergerFactoryBean">
    <property name="listOfLists">
        <list>
            <ref bean="listA" />
            <ref bean="listB" />
        </list>
    </property>
</bean>

java:

public class ListMergerFactoryBean implements FactoryBean<List> {

private List<List> listOfLists;

@Override
public List getObject() throws Exception {
    List mergedList = new ArrayList();
    for (List list : listOfLists) {
            mergedList.addAll(list);
    }
    return mergedList;
}

@Override
public Class<?> getObjectType() {
    return (new ArrayList()).getClass();
}

@Override
public boolean isSingleton() {
    return false;
}

public void setListOfLists(List<List> listOfLists) {
    this.listOfLists = listOfLists;
}

}

UPDATE: I have eliminated a bug using Aron Bartle's solution. Thanks!

Community
  • 1
  • 1
BTakacs
  • 2,397
  • 3
  • 24
  • 26
2

There is a sublte bug with the ListMergerFactoryBean solution above.

Config:

<util:list id="listA" value-type="java.lang.String">
  <value>fooA</value>
  <value>barA</value>
</util:list>
<util:list id="listB" value-type="java.lang.String">
  <value>fooB</value>
  <value>barB</value>
</util:list>
<util:list id="listC" value-type="java.lang.String">
  <value>fooC</value>
  <value>barC</value>
</util:list>
<bean id="AwithB" class="com.util.ListMergerFactoryBean">
  <property name="listOfLists">
    <list>
        <ref bean="listA" />
        <ref bean="listB" />
    </list>
  </property>
</bean>
<bean id="AwithC" class="com.util.ListMergerFactoryBean">
  <property name="listOfLists">
    <list>
        <ref bean="listA" />
        <ref bean="listC" />
    </list>
  </property>
</bean>

With this config you might be surprised when bean AwithB has the expected content of AwithC. ListA is a singleton and the getObject method alters it, therefore alters it for all users of ListMergerFactoryBean, even though the bean factory is not a singleton. The fix is to not re-use the first list in the listOfLists:

@Override
public List getObject() throws Exception {
  List mergedList = new ArrayList();
  for (List list : listOfLists) {
    mergedList.addAll(list);
  }
  return mergedList;
}