Spring tips: How to merge collections in spring xml?

Hello guys, today we are going to discover how to merge collections in spring.xml. So lets imagine the situation that you are developing 2 a bit different websites “potatoSite” and “tomatoSite” using spring. And you created a a service that clean ups data.

interface CleanUpService {
    void cleanUp();
    void setDirsToCleanUp(Set<String> dirs);
}

You made your service configurable so you can set up list of directories to clean up. So the initial setup was following:

<bean id="cleanUpService" class="x.y.z.CleanUpServiceImpl">
	<property name="dirsToCleanUp">
		<set>
			<value>media</value>
			<value>logs</value>
		</set>
	</property>
</bean>

But then a new requirement appeared and now for different sites you have to use 2 different set of directories. Since you already had profiles for different sites you decided to put beans under profile:

<beans profile="tomatoSite">
    <bean id="cleanUpService" class="x.y.z.CleanUpServiceImpl">
        <property name="dirsToCleanUp">
            <set>
                <value>media</value>
                <value>logs</value>
                <value>archived</value>
            </set>
        </property>
    </bean>
</beans>

<beans profile="potatoSite">
    <bean id="cleanUpService" class="x.y.z.CleanUpServiceImpl">
        <property name="dirsToCleanUp">
            <set>
                <value>media</value>
                <value>logs</value>
                <value>cache</value>
            </set>
        </property>
    </bean>
</beans>

It looks good, does not it? Everything what is required implemented. It seems that we can stop. But no, we have a duplication of configs. So we have a choice:

  • to create a parent bean for service with common set of dirs and extend it in child beans under spring profile
  • to create a parent bean for sets and extend it in child beans.

Lets consider both options. The first is option with service as a parent bean and extending dirToCleanUp property in child beans:

<bean id="baseCleanUpService" class="x.y.z.CleanUpServiceImpl" abstract="true">
    <property name="dirsToCleanUp">
        <set>
            <value>media</value>
            <value>logs</value>
        </set>
    </property>
</bean>

<beans profile="tomatoSite">
    <bean id="cleanUpService" parent="baseCleanUpService">
        <property name="dirsToCleanUp">
            <set merge="true">
                <value>archived</value>
            </set>
        </property>
    </bean>
</beans>

<beans profile="potatoSite">
    <bean id="cleanUpService" parent="baseCleanUpService">
        <property name="dirsToCleanUp">
            <set merge="true"> 
                <value>cache</value> 
             </set> 
        </property> 
    </bean> 
</beans> 

Merge attribute is presented for list, map, set or props elements. It covers the most cases you have in spring application.

The way I showed above is more classical way, sometimes you can’t follow it e.g. you don’t have spring profiles, but you need to implement such feature. What you going to do? I assume solution can be the following:

<bean id="cleanUpService" class="x.y.z.CleanUpServiceImpl">
    <property name="dirsToCleanUp" ref="${site.id}-dirs"/>
</bean>

<bean id="commonDirs" class="org.springframework.beans.factory.config.SetFactoryBean">
    <property name="sourceSet">
        <set>
            <value>media</value>
            <value>logs</value>
        </set>
    </property>
</bean>

<bean id="popato-dirs" parent="commonDirs">
    <property name="sourceSet">
        <set merge="true">
            <value>cache</value>
        </set>
    </property>
</bean>

<bean id="tomato-dirs" parent="commonDirs">
    <property name="sourceSet">
        <set merge="true">
            <value>archived</value>
        </set>
    </property>
</bean>

The snippet above has 2 interesting things:

  • You can use FactoryBean to inherit set property. Know child classes of Factory bean are: ListFactoryBean, MapFactoryBean, SetFactoryBean, SortedResourcesFactoryBean etc.
  • You can use values from property file to define bean name. In example above ${site.id}-dirs will be evaluated dynamically based on included source of .properties file and if you have site.id=tomato that tomato-dirs bean will be used.

Basically that is it for today. I understand that situation may look like unreal and I guess most of developer would just extract list of directories into property file, but now you know that such feature in spring exist and you can use it. Thanks.

Advertisements

Author: nikitapavlenko

I am Software Engineer from Kharkiv, Ukraine. I develop e-commerce web applications using Hybris platfrom mostly using Java.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s