@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.