I’m a big user of Spring, and I must confess I didn’t follow all the latest additions of version 3.1. One such addition is the notion of profile.
Context
Profiles are meant to address the case when you use the same Spring configuration across all your needs, but when there are tiny differences.
The most frequent use-case encountered is the datasource.
For example, during my integration tests, I’m using no application server so my datasource comes from a simple org.apache.commons.dbcp.BasicDataSource
configured with URL, driver class name, user and password like so:
<bean id="dataSource">
<property name="driverClassName" value="${db.driver}" />
<property name="url" value="${db.url}" />
<property name="username" value="${db.username}" />
<property name="password" value="${db.password}" />
</bean>
Note there are other alternatives to BasicDataSource
, such as org.springframework.jdbc.datasource.SingleConnectionDataSource
or com.mchange.v2.c3p0.ComboPooledDataSource
.
In an application server environment, I use another bean definition:
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/ds" />
You probably noticed both previous bean definitions have the same name. Keep it in mind, it will play a role in the next section.
My legacy solution
In order to be able to switch from one bean to another in regard to the context, I use the following solution:
- I separate the datasource bean definition in its own Spring configuration file (respectively spring-datasource-it.xml and spring-datasource.xml)
- For production code, I create a main Spring configuration file that imports the latter:
<!--?xml version="1.0" encoding="UTF-8"?--> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="..."> <import resource="classpath:spring-datasource.xml"/> ... </beans>
- For integration testing, I use a Spring Test class as parent (like
AbstractTestNGSpringContextTests
) and configure it with theContextConfiguration
annotation.@ContextConfiguration
has a locationattribute
that can be set with the location of all needed Spring configuration fragments for my integration test to run.
Besides, as a Maven user, I can neatly store the spring-datasource.xml file under src/main/resources
and the spring-datasource-it.xml file under src/test/resources
, or in separate modules.
Given these, the final artifact only contains the relevant application server datasource bean in my Spring configuration: the basic datapool safely stays in my test code.
The profile solution
Remember when bean identifiers had to be unique across your entire Spring configuration, this is the case no more.
With profiles, each bean (or more precisely beans group) can be added an extra profile information so that bean identifiers have to be unique across a profile.
This means that we can define two beans with the datasource
identifier, and set a profile for each: Spring won’t complain.
If we call those profiles integration
and application-server
, and activate those in the code when needed, this will have exactly the same results.
The Spring configuration file will look like this:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="...">
...
<beans profile="integration">
<bean id="dataSource">
<property name="driverClassName" value="${db.driver}" />
<property name="url" value="${db.url}" />
<property name="username" value="${db.username}" />
<property name="password" value="${db.password}" />
</bean>
</beans>
<beans profile="production">
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/ds" />
</beans>
</beans>
Now, the Spring configuration file contains both contexts.
Conclusion
I’m not entirely sure that such a use of profiles brings anything to the table (despite the datasource example being depicted everywhere on the web). Granted, we switched from a build-time configuration to a runtime configuration. But the only consequence I see is that the final artifacts ships with information irrelevant to the environment: it’s bad practice at best, and at worst a security flaw.
Other use-cases could include the need to run the application both on an application server and in the cloud. Even then, I would be a in favor of distinct configuration files assembled at build time. I think I will stay with my legacy approach for the moment, at least until I’m proven wrong or until a case presents itself that can easily be solved by profiles without side-effects.