Using Spring Profiles in Production

DZone 's Guide to

Using Spring Profiles in Production

· Integration Zone ·
Free Resource

Lately I discovered spring profiles and I fell in love. It solves several different problems we had, it was easy to use and it works very well.

What Are Spring Profiles?

Spring profiles give you the power to conditionally load spring beans. You define your bean inside a profile, either by annotation within the source code, or in the spring XML file. Then you can load your spring context with list of active profiles. Beans with profile definitions, that their profile is not active, will not load.

More information is in the formal documentation, here.

What is it Good for?

Well, you'll see many examples in the internet to have different set of beans for production or for test environment (e.g. mocks), but I found it very useful for different behaviors in production as well. Let me give you three examples. The first example shows how to select between behaviors, using spring XML files, the second one shows how to do that using spring annotations, and the last one shows how to enable/disable a behavior by spring profile.

Multiple Database Implementations

Our application should support multiple types of databases. For each deployment, the application will work with only one type of database. However, creating database layers for both mongoDB and oracle DB (for example) can cause many problems. For instance, mongoDB client keeps trying to connect to the database, but it wasn't there when we deployed the application with oracle. When we worked with mongoDB, the application mistakenly was using the Transactional annotation (only relevant in Oracle implementation), and kept trying to connect to the oracle database.

In order to solve it we created two database implementations, both implementing the same interface. For each one of the database implementations, we created a different spring XML file, for example:

<beans xmlns="http://www.springframework.org/schema/beans"  
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"  
   default-lazy-init="true" default-init-method="startup"  
   default-destroy-method="shutdown" profile="ORACLE_DATASOURCE">

Within these files, we could create beans from classes that implement the interfaces. We could add additional beans specific for this type of database. We could even reuse the bean names to ease the loading, because there is no problem with naming conflicts, as the other database layer beans will never load.

In this case, we also imported XML files from other sources (e.g. infrastructure database libraries). As the import lines are under the profile definition, Spring will not import these database specific XMLs and their beans in the other profile where they're not relevant.

Project Specific Plugin Using jax-rs Resources

The application is a HTTP server implemented by jersey framework for jax-rs. We had to support changes in the server behavior for the same HTTP requests. However, jersey won't allow us to have two resources with the same path. The solution was to define the resource class as a spring component, and use profiles in order to load only the requested class.

public class Generic_ItemResourceextends AbstractItemResource {  

Then we can define the plugin version of this resource:

public class Plugin_ItemResource extends AbstractItemResource {  

The beans are using the same name - "itemsResource" - and so we can use this resource as a nested resource, but only the relevant instance will be loaded and used, for example:

public class ApplicationResource {  
     AbstractItemResource  itemResource;  

     public AbstractItemResource getItemResource() {  
          return itemResource;  

The @Profile annotation may contain more complex expressions than just the profile name. It could be a list of names:

@Profie({"PRFILE_A", "PROFILE_B"})

Or even negative expression:

// any bean that is not defined in "PROFILE_A" will be loaded

Enabling/Disabling Quartz

In our application, we should support housekeeping task, for example, cleaning the database from redundant information. We can run the application in three modes: 'server-only' to only respond to HTTP requests, 'housekeeping-only' for the background task and 'standalone' for both. We can use this mode for two deployment models - one where we have several server-only instances and one housekeeping-only instance, and in other deployment models, we only use one standalone instance.

We don't want the quartz thread to work in a server-only mode. So we defined all the housekeeping beans under HOUSEKEEPING profile. we use this profile in housekeeping-only and in standalone modes, but not in server-only mode. The result is that quartz beans do not load and the thread is not created and does not run.

Choose Active Profiles on Runtime

In our main method, before loading the spring context, we first define the requested profile, according to the current configuration.

final boolean dsDataSource = "mongodb".equals( configuration.getString( "application.datasource", "mongodb") ); 

String datasourceProfile = mongoDbDataSource ? DbLayerMongoDB.PROFILE_NAME : DbLayerOracle.PROFILE_NAME; 

String pluginName = configuration.getString("application.plugin.name"); 

List<String> profiles = new ArrayList<>(); 



String operationMode = configuration.getString("application.operationmode");

if ( !"SERVER_ONLY".equals(operationMode) ) { 

String[] activeProfiles = profiles.toArray(new String[profiles.size()]); 

GenericXmlApplicationContext context = new GenericXmlApplicationContext(); 




Unit Tests

OK, so our application work great with spring profiles. But the unit tests fails because the active profiles are defined in the main method, and all the beans with profiles won't be loaded in the test. So here is how to set the active profiles in junit:

@ContextConfiguration(locations = { "classpath*:META-INF/mySpringContext.xml" })  
public class TestMyApplication {  

enterprise-integration ,integration ,java ,spring ,spring profiles

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}