Spring @Profile Guide

In this post we will explore Spring @Profile annotation and use it to make different set of beans or configuration available conditionally on different environments. We will also discuss corresponding XML based profile configuration for comparison.

Let’s imagine you have an application which includes database-interaction. You might want to configure one dataSource (for MySQL e.g.) for Development environment while completely different dataSource (for ORACLE e.g.) for Production.

Using Spring Profiles, you can easily manage such setup. We will explore this example use-case in this post.


Following technologies being used:

  • Spring 4.0.6.RELEASE
  • Maven 3
  • JDK 1.6
  • Eclipse JUNO Service Release 2

Project directory structure

Following will be the final project directory structure for this example:

Let’s now add the content mentioned in above structure explaining each in detail.

Step 1: Provide Spring dependencies in Maven pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.websystique.spring</groupId>
	<artifactId>Spring4ProfilesExample</artifactId>
	<version>1.0.0</version>
	<packaging>jar</packaging>

	<name>Spring4ProfilesExample</name>

	<properties>
		<springframework.version>4.0.6.RELEASE</springframework.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${springframework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${springframework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>${springframework.version}</version>
		</dependency>

	</dependencies>
	<build>
		<pluginManagement>
			<plugins>
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-compiler-plugin</artifactId>
					<version>3.2</version>
					<configuration>
						<source>1.6</source>
						<target>1.6</target>
					</configuration>
				</plugin>
			</plugins>
		</pluginManagement>
	</build>

</project>

Step 2: Create Spring Configuration Class

Spring configuration class are the ones annotated with @Configuration. These classes contains methods annotated with @Bean. These @Bean annotated methods generates beans managed by Spring container.


package com.websystique.spring.configuration;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages = "com.websystique.spring")
public class AppConfig {
	
	@Autowired
	public DataSource dataSource;
	

}


Above Configuration just have one property to be auto-wired. Now what we will show here is that this dataSource bean can be injected with different beans on different environment (MySQL dataSource on Development & ORACLE for Production e.g.).


package com.websystique.spring.configuration;

import javax.sql.DataSource;

public interface DatabaseConfig {

	DataSource createDataSource();
	
}

Simple Interface to be implemented by all possible environment configurations.


package com.websystique.spring.configuration;

import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

@Profile("Development")
@Configuration
public class DevDatabaseConfig implements DatabaseConfig {

	@Override
	@Bean
	public DataSource createDataSource() {
		System.out.println("Creating DEV database");
		DriverManagerDataSource dataSource = new DriverManagerDataSource();
		/*
		 * Set MySQL specific properties for Development Environment
		 */
		return dataSource;
	}

}


package com.websystique.spring.configuration;

import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

@Profile("Production")
@Configuration
public class ProductionDatabaseConfig implements DatabaseConfig {

	@Override
	@Bean
	public DataSource createDataSource() {
		System.out.println("Creating Production database");
		DriverManagerDataSource dataSource = new DriverManagerDataSource();
		/*
		 * Set ORACLE specific properties for Production environment
		 */
		return dataSource;
	}

}

Both DevDatabaseConfig and ProductionDatabaseConfig are simple configuration classes implementing DatabaseConfig interface. What special about these classes are that they are annotated with @Profile annotation.

@Profile annotation on a component registers that component in Spring context only when that profile is active. Profile activation means this profile value should be available either by

  • Setting spring.profiles.active property (via JVM arguments, environment variable or Servlet context parameter in web.xml in case of web applications)
  • ApplicationContext.getEnvironment().setActiveProfiles(“ProfileName”);

Based on your environment you will provide the value of profile, and the beans dependent on that profile will be registered in Spring container.

Step 3: Create Main to run as Java Application

package com.websystique.spring;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class AppMain {
	
	public static void main(String args[]){
		AnnotationConfigApplicationContext  context = new AnnotationConfigApplicationContext();
		//Sets the active profiles
		context.getEnvironment().setActiveProfiles("Development");
		//Scans the mentioned package[s] and register all the @Component available to Spring
		context.scan("com.websystique.spring"); 
		context.refresh();
		context.close();
	}

}

Notice how we have set the active profiles to be used on runtime. context.scan("package") scans the mentioned package and registers all the @Component but when it encounters the @Profile annotation on a bean/configuration, it compare the profile value with the one supplied in environment. If the value mathces, it registers that bean else it skips it. In our case it’s the DevDatabaseConfig dataDource which will be injected in AppConfig.dataSource.

Run above program , you will see following output:

Creating DEV database

That’s it.

XML Based Spring Profile Configurtrion

Now, Let’s discuss Spring Profile once more, this time using XML configuration

Replace DevelopmentDatabaseConfig by dev-config-context.xml (src/main/resources/dev-config-context.xml)

<?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="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">

	
	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="jdbc:mysql://localhost:3306/websystique" />
		<property name="username" value="myuser" />
		<property name="password" value="mypassword" />
  	</bean>

</beans>

Replace ProductionDatabaseConfig by prod-config-context.xml (src/main/resources/prod-config-context.xml)

<?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="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">

	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value=" oracle.jdbc.driver.OracleDriver" />
		<property name="url" 	value="jdbc:oracle:thin:@PRODHOST:PRODPORT/websystique" />
		<property name="username" value="myproduser" />
		<property name="password" value="myprodpassword" />
	</bean>

</beans>

Replace AppConfig by app-config.xml (src/main/resources/app-config.xml)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        					http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

	
	<context:component-scan base-package="com.websystique.spring"/>
	
	<beans profile="Development">
		<import resource="dev-config-context.xml"/>
	</beans>

	<beans profile="Production">
		<import resource="prod-config-context.xml"/>
	</beans>

</beans>

Based on actual profile in use, corresponding config-context.xml file will be loaded, other one will be ignored.

Let’s also tweak the main:

package com.websystique.spring;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AppMain {
	
	public static void main(String args[]){
		AbstractApplicationContext  context = new ClassPathXmlApplicationContext("app-config.xml");
		//Sets the active profiles
		context.getEnvironment().setActiveProfiles("Development");
		/*
		 * Perform any logic here
		 */
		context.close();
	}

}

Run it. You will see the same output as with Annotation based setup.

Download Source Code


References