Spring Profiles

0

Profiles

Spring profiles provide a mechanism in the core container that allows for registration of different beans in different environments. In this article, we will go through the profile algorithm and the different ways of using it.
The profile attribute is part of beans element. If the beans element doesn’t have a profile attribute, then it will always be loaded irrespective of the active profiles set.
If it has a profile, then the active profiles must have at least one of those profile else the beans will not be loaded.
There can be multiple active profiles and multiple profiles in profile attribute. The profiles need to be either delimited by space or comma delimited or semi-colon like profile="dev" or profile="dev test" or profile="dev, test" or profile="dev; test".

Spring Profiles
Spring Profiles

No profiles attribute

What happens if active profiles are set but the context doesn’t have profiles attribute? Lets checks it out using an example.
My context file is testNoProfileAttribute.xml:

<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-3.1.xsd">

    <bean id="foo" class="springdemo.Foo"/>
</beans>

Active profile is set to dev. There is no profile attribute but we should still be able to get the bean.

    public void testContextWithNoProfileAttribute() {
        GenericXmlApplicationContext context = new GenericXmlApplicationContext();
        context.getEnvironment().setActiveProfiles("dev");
        context.load("springdemo/testNoProfileAttribute.xml");
        context.refresh();
        assertTrue(context.containsBean("foo"));
    }

Profile attribute specified in nested beans element

Now we look into another example where profiles attribute is set in the nested beans element.
In the below context file, bar has two flavors of implementation depending on the profile.

Here is the context file: testProfile.xml

<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-3.1.xsd">

    <bean id="foo" class="springdemo.Foo">
        <property name="bar" ref="bar"/>
    </bean>
    <beans profile="prod">
        <bean id="bar" class="springdemo.ProdBar"/>
    </beans>
    <beans profile="dev">
        <bean id="bar" class="springdemo.DevBar"/>
    </beans>
</beans>

We set the active profiles programatically. If active profile is dev, we get DevBar. If active profile is set to prod, we can ProdBar.

    public void testProfileUsingBeansSubElement() {
        GenericXmlApplicationContext context = new GenericXmlApplicationContext();
        context.getEnvironment().setActiveProfiles("dev");
        context.load("springdemo/testProfile.xml");
        context.refresh();
        assertTrue(context.containsBean("foo"));
        Foo foo = (Foo) context.getBean("foo");
        assertTrue(foo.getBar() instanceof DevBar);

        context = new GenericXmlApplicationContext();
        context.getEnvironment().setActiveProfiles("prod");
        context.load("springdemo/testProfile.xml");
        context.refresh();
        assertTrue(context.containsBean("foo"));
        foo = (Foo) context.getBean("foo");
        assertTrue(foo.getBar() instanceof ProdBar);


        // We can also use ClassPathXmlApplicationContext
        ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext();
        appContext.getEnvironment().setActiveProfiles("dev");
        appContext.setConfigLocation("springdemo/testProfile.xml");
        appContext.refresh();
        assertTrue(appContext.containsBean("foo"));
        foo = (Foo) appContext.getBean("foo");
        assertTrue(foo.getBar() instanceof DevBar);
    }

No active profiles set

What happens if the context file is meant for a certain profile but not active profile is set?
Below example shows if the active profile is set, then it will result in bean creation failure while trying to create bean definition for “bar”.

    public void testContextWithNoActiveProfiles() {
        try {
            GenericXmlApplicationContext context = new GenericXmlApplicationContext();
            context.load("springdemo/testProfile.xml");
            context.refresh();
        } catch (BeanCreationException e) {
        }
    }

Profile context decoupled

In the below example, we decouple the profile based implementations from the common context and move into separate context files.

The common classes go here:

testProfileCommon.xml:

<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-3.1.xsd">

    <bean id="foo" class="springdemo.Foo">
        <property name="bar" ref="bar"/>
    </bean>
</beans>

Dev based classes go here:

testDevProfile.xml:

<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-3.1.xsd">
    <beans profile="dev">
        <bean id="bar" class="springdemo.DevBar"/>
    </beans>
</beans>

Prod based classes go here:

testProdProfile.xml:

<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-3.1.xsd">
    <beans profile="prod">
        <bean id="bar" class="springdemo.ProdBar"/>
    </beans>
</beans>

The active profiles can be set either programatically or using a system property.
We will look into both the cases in the below test case.

Using setter method

    public void testProfileUsingSetter() {
        GenericXmlApplicationContext context = new GenericXmlApplicationContext();
        context.getEnvironment().setActiveProfiles("dev");
        context.load("springdemo/testProfileCommon.xml",
                "springdemo/testDevProfile.xml",
                "springdemo/testProdProfile.xml");
        context.refresh();
        assertTrue(context.containsBean("foo"));
        Foo foo = (Foo) context.getBean("foo");
        assertTrue(foo.getBar() instanceof DevBar);

        context = new GenericXmlApplicationContext();
        context.getEnvironment().setActiveProfiles("prod");
        context.load("springdemo/testProfileCommon.xml",
                "springdemo/testDevProfile.xml",
                "springdemo/testProdProfile.xml");
        context.refresh();
        assertTrue(context.containsBean("foo"));
        foo = (Foo) context.getBean("foo");
        assertTrue(foo.getBar() instanceof ProdBar);

    }

Using system property

Profiles can be set externally using system property spring.profiles.active

    public void testProfileUsingSystemProperty() {
        System.setProperty("spring.profiles.active", "dev");
        ClassPathXmlApplicationContext appContext =
                new ClassPathXmlApplicationContext("springdemo/testProfileCommon.xml",
                "springdemo/testDevProfile.xml",
                "springdemo/testProdProfile.xml");
        Foo foo = (Foo) appContext.getBean("foo");
        assertTrue(foo.getBar() instanceof DevBar);

        System.setProperty("spring.profiles.active", "prod");
        appContext =
                new ClassPathXmlApplicationContext("springdemo/testProfileCommon.xml",
                        "springdemo/testDevProfile.xml",
                        "springdemo/testProdProfile.xml");
        foo = (Foo) appContext.getBean("foo");
        assertTrue(foo.getBar() instanceof ProdBar);

        System.setProperty("spring.profiles.active", "dev");
        appContext =
                new ClassPathXmlApplicationContext("springdemo/testProfileCommon.xml",
                        "springdemo/*Profile.xml");
        foo = (Foo) appContext.getBean("foo");
        assertTrue(foo.getBar() instanceof DevBar);
    }

Multiple profiles

If we want DevBar to be the implementation used for both dev and test profiles, we can include them in profile attribute as profile="dev,test".

In the below context file testMultiProfile.xml we have multiple profiles sharing the beans

<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-3.1.xsd">

    <bean id="foo" class="springdemo.Foo">
        <property name="bar" ref="bar"/>
    </bean>
    <beans profile="prod">
        <bean id="bar" class="springdemo.ProdBar"/>
    </beans>
    <beans profile="dev,test">
        <bean id="bar" class="springdemo.DevBar"/>
    </beans>
</beans>

In both dev and test profiles, we get the same type of Bar.

    public void testMultiProfile() {
        System.setProperty("spring.profiles.active", "dev");
        ClassPathXmlApplicationContext appContext =
                new ClassPathXmlApplicationContext("springdemo/testMultiProfile.xml");
        Foo foo = (Foo) appContext.getBean("foo");
        assertTrue(foo.getBar() instanceof DevBar);

        System.setProperty("spring.profiles.active", "test");
        appContext =
                new ClassPathXmlApplicationContext("springdemo/testMultiProfile.xml");
        foo = (Foo) appContext.getBean("foo");
        assertTrue(foo.getBar() instanceof DevBar);
    }
Share.

Leave A Reply