Spring @ContextHierarchy Annotation Example

0

@ContextHierarchy is a class-level annotation that is used to define a hierarchy of contexts using a list of @ContextConfiguration instances, each of which defines a level in the context hierarchy. In this article we will see different ways of establishing application context hierarchy using @ContextHierarchy. We will also see how to inherit, merge or override an existing context.

Single Context

Let’s first define single ApplicationContext.

SpringSingleContext:

package com.javarticles.spring;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
@ContextConfiguration(locations = "/context1.xml")
public class SpringSingleContext {
    @Autowired
    BeanA beanA;
}

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

	<bean id="beanA" class="com.javarticles.spring.BeanA" />
</beans>

Define Context hierarchy

We extend SpringSingleContext and define multiple contexts using @ContextHierarchy. It now consists of three levels, first one is for context1.xml, the second one is for context2.xml and third one is for context3.xml.

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

	<bean id="beanB" class="com.javarticles.spring.BeanB" />
</beans>

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

	<bean id="beanC" class="com.javarticles.spring.BeanC" />

</beans>

SpringMultipleContextsExample:

package com.javarticles.spring;

import static org.junit.Assert.*;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextHierarchy({
        @ContextConfiguration("/com/javarticles/spring/context2.xml"),
        @ContextConfiguration("/com/javarticles/spring/context3.xml") })
public class SpringMultipleContextsExample extends SpringSingleContext {
    @Autowired
    private BeanB beanB;

    @Autowired
    private BeanC beanC;

    @Autowired
    private ApplicationContext context;

    @Test
    public void verifyBeans() {        
        assertNotNull(beanA);
        assertNotNull(beanB);
        assertNotNull(beanC);
    }
    
    @Test
    public void printAllContexts() {
        System.out.println("Print contexts " + context);
        ApplicationContext parent = context;
        while (parent != null) {
            System.out.println("Context: " + parent.getId());
            System.out.println("[");
            for (String beanName : parent.getBeanDefinitionNames()) {
                if (beanName.contains("bean")) {
                    System.out.println(beanName);
                }
            }
            System.out.println("]");
            parent = parent.getParent();
        }
    }
}

Output:

Print contexts [email protected]cf57e3: startup date [Sun Feb 07 23:15:22 IST 2016]; parent: [email protected]e6e9c3
Context: [email protected]cf57e3
[
beanC
]
Context: [email protected]e6e9c3
[
beanB
]
Context: [email protected]56dfcb
[
beanA
]

Inherit context hierarchy using @ContextHierarchy

In the below test class, we have defined two contexts, a parent and a child. We have given names to each context, parent is context1 and child is context2.

The ApplicationContext is autowired using @Autowired annotation. If there are multiple contexts, the test instance is autowired with one of the lowest context in the hierarchy. In our case, it should be the context2.

SpringBaseContextHierarchy:

package com.javarticles.spring;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextHierarchy({
@ContextConfiguration(name="context1", locations = "/context1.xml"),
@ContextConfiguration(name="context2", locations = "/com/javarticles/spring/context2.xml")})
public class SpringBaseContextHierarchy {

    @Autowired
    ApplicationContext context;
    
    @Test
    public void printAllContexts() {
        System.out.println("Print contexts " + context);
        ApplicationContext parent = context;
        while (parent != null) {
            System.out.println("Context: " + parent.getId());
            System.out.println("[");
            for (String beanName : parent.getBeanDefinitionNames()) {
                if (beanName.contains("bean")) {
                    System.out.println(beanName);
                }
            }
            System.out.println("]");
            parent = parent.getParent();
        }
    }
}

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

	<bean id="beanA" class="com.javarticles.spring.BeanA" />

</beans>

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

	<bean id="beanB" class="com.javarticles.spring.BeanB" />
</beans>

You can see from the output, the context autowired is the context2.

Output:

Print contexts [email protected]24753a: startup date [Sun Feb 07 22:39:35 IST 2016]; parent: [email protected]a485d2
Context: [email protected]24753a
[
beanB
]
Context: [email protected]a485d2
[
beanA
]

We will next inherit this test class to further add new application contexts. We have added a new context context3. If you notice, we have added a new context XML to an existing context context1. By default, the new XML will added to the existing context instead of creating a new application context.

SpringInheritContextExample:

package com.javarticles.spring;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextHierarchy({
        @ContextConfiguration(name = "context1", locations = "/com/javarticles/spring/context4.xml"),
        @ContextConfiguration(name = "context3", locations = "/com/javarticles/spring/context3.xml") })
public class SpringInheritContextExample extends SpringBaseContextHierarchy {

    @Autowired
    private BeanC beanC;

    @Test
    public void beanADetails() {
        assertFalse(context.containsBeanDefinition("beanA"));
        assertTrue(context.getParent().containsBean("beanA"));
        System.out.println("Instance of beanA: " + context.getBean("beanA"));
    }

    @Test
    public void verifyBeans() {
        assertNotNull(context.getBean("beanB"));
        assertNotNull(context.getBean("beanC"));
        assertNotNull(context.getBean("beanD"));
    }
}

Here are the context files.

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

	<bean id="beanC" class="com.javarticles.spring.BeanC" />

</beans>

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

	<bean id="beanD" class="com.javarticles.spring.BeanD" />
	<bean id="beanA" class="com.javarticles.spring.OverridenBeanA" />

</beans>

When you run the test class, you can see from the output that the context2 now has two beans BeanA and BeanD. Also a new child context has been added.

Output:

Instance of beanA: [email protected]
Print contexts [email protected]f6480: startup date [Mon Feb 08 08:44:03 IST 2016]; parent: [email protected]c7c2a6
Context: [email protected]f6480
[
beanC
]
Context: [email protected]c7c2a6
[
beanB
]
Context: [email protected]7c004
[
beanA
beanD
]

Override context using @ContextHierarchy

Let’s now override context1 with the following context XML.

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

	<bean id="beanD" class="com.javarticles.spring.BeanD" />	

</beans>

In order to override a context set @ContextConfiguration‘s attribute inheritLocations to false.

SpringOverrideContextExample:

package com.javarticles.spring;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextHierarchy({
        @ContextConfiguration(name = "context1", 
                locations = "/com/javarticles/spring/overrideContext.xml", 
                inheritLocations = false),
        @ContextConfiguration(name = "context3", locations = "/com/javarticles/spring/context3.xml") })
public class SpringOverrideContextExample extends SpringBaseContextHierarchy {

    @Autowired
    private BeanC beanC;

    @Test
    public void beanAShouldntExist() {
        assertFalse(context.containsBean("beanA"));
    }

    @Test
    public void verifyBeans() {
        assertNotNull(context.getBean("beanB"));
        assertNotNull(context.getBean("beanC"));
        assertNotNull(context.getBean("beanD"));
    }
}

Output:

Print contexts [email protected]adae5d: startup date [Mon Feb 08 08:59:27 IST 2016]; parent: [email protected]09711b
Context: [email protected]adae5d
[
beanC
]
Context: [email protected]09711b
[
beanB
]
Context: [email protected]c7c2a6
[
beanD
]

If inheritLocations is set to true, output will be different.

Output:

Print contexts [email protected]f6480: startup date [Mon Feb 08 09:01:32 IST 2016]; parent: [email protected]c7c2a6
Context: [email protected]f6480
[
beanC
]
Context: [email protected]c7c2a6
[
beanB
]
Context: [email protected]7c004
[
beanA
beanD
]

Download the source code

This was an example about using spring @ContextHierarchy to create hierarchy of contexts.

You can download the source code here: springContextHierarchyAnnotationExample.zip
Share.

Comments are closed.