Spring Nested Beans Example

0

In this article, we will see some examples of nested beans. A nested beans element has the ability to override the outer beans behavior. We will use nested beans to override lifecycle methods. We will also see how we can use it along with the spring profiles to completely override a bean definition.

This example uses the following frameworks:

  1. Maven 3.2.3
  2. Spring 4.1.5.RELEASE
  3. Eclipse  as the IDE, version Luna 4.4.1.

Dependencies

Add the following dependencies:

  1. spring-core
  2. spring-context

Override the lifecycle methods using nested beans

In this example, we will override the lifecycle methods using nested beans. TestBean contains different flavors of init and destroy methods. We will use one set for the default init and destroy callbacks. We will use the other sets to override the lifecycle methods in our nested beans.

TestBean:

package com.javarticles.spring;

public class TestBean {
    private String name;
   
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void init() {
        System.out.println("init() called");
    }
    
    public void destroy() {
        System.out.println("destroy() called");
    }
    
    public void init1() {
        System.out.println("init1() called");
    }
    
    public void destroy1() {
        System.out.println("destroy1() called");
    }
    
    public void init2() {
        System.out.println("init2() called");
    }
    
    public void destroy2() {
        System.out.println("destroy2() called");        
    }   
    
    public String toString() {
        return name == null ? "No Name" : name;
    }
}

The main beans element has default-init-method and default-destroy-method set to init and destroy methods.
We override it using nested beans to init1 and destroy1. We have used one more level of nested beans to further override the behavior to call init2 and destroy2.

nestedBeansContext.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-3.1.xsd"
	default-init-method="init" default-destroy-method="destroy" default-lazy-init="true">

	<bean id="bean1" class="com.javarticles.spring.TestBean" />

	<beans default-init-method="init1" default-destroy-method="destroy1">
		<bean id="bean2" class="com.javarticles.spring.TestBean" />

		<beans default-init-method="init2" default-destroy-method="destroy2">
			<bean id="bean3" class="com.javarticles.spring.TestBean" />
		</beans>

		<beans>
			<bean id="bean4" class="com.javarticles.spring.TestBean" />
		</beans>
	</beans>	
</beans>
  1. bean1 will use the default behavior set.
  2. bean2‘s behavior is overridden. The default init and destroy methods are init1 and destroy1.
  3. bean3‘s behavior is again overridden. The default init and destroy methods are init2 and destroy2.
  4. bean4‘s uses the outer beans element’s default init and destroy methods and they are init1 and destroy1.

SpringNestedBeansExample:

package com.javarticles.spring;

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

public class SpringNestedBeansExample {
    public static void main(String[] args) {
        System.out.println("Nested beans Example");
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
                "nestedBeansContext.xml");
        System.out.println("bean1's lifecycle methods will be the default init and destory methods");        
        verifyLifecycleMethods("bean1", context);
        
        System.out.println("bean2's lifecycle methods will be overriden by bean level init1 and destroy1 methods");
        verifyLifecycleMethods("bean2", context);
        
        System.out.println("bean3's lifecycle methods will be overriden by nested beans init2 and destroy2 methods");
        verifyLifecycleMethods("bean3", context);
        
        System.out.println("bean4's lifecycle methods will be the outer beans init1 and destroy1 methods");
        verifyLifecycleMethods("bean4", context);
    }
    
    private static void verifyLifecycleMethods(String beanId, ConfigurableApplicationContext context) {
        Object bean = null;
        try {                       
            bean = context.getBean(beanId);            
        } finally {
            context.getBeanFactory().destroyBean(beanId, bean);
        }
    }
}

Output:

bean1's lifecycle methods will be the default init and destory methods
init() called
destroy() called
bean2's lifecycle methods will be overriden by bean level init1 and destroy1 methods
init1() called
destroy1() called
bean3's lifecycle methods will be overriden by nested beans init2 and destroy2 methods
init2() called
destroy2() called
bean4's lifecycle methods will be the outer beans init1 and destroy1 methods
init1() called
destroy1() called

Override bean definition using nested beans

We may need certain beans when it is developer’s environment. We may also have to override certain beans for the developer environment to simplify testing.
In this example, we will override the bean definitions using nested beans and spring profiles.

Below context XML contains a nested beans element for dev profile. beanA is of type TestBean whereas in the dev profile it is just a plain string.
We also have an additional devOnlyBean bean in dev profile.

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

	<bean id="beanA" class="com.javarticles.spring.TestBean">
		<property name="name" value="Actual bean object"/>
	</bean>

	<beans profile="dev">
		<bean name="beanA" class="java.lang.String">
			<constructor-arg value="Simple string for dev"/>
		</bean>

		<bean name="devOnlyBean" class="java.lang.String">
			<constructor-arg value="Only available for dev profile"/>
		</bean>
	</beans>
</beans>

SpringNestedBeansProfileExample:

package com.javarticles.spring;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringNestedBeansProfileExample {
    public static void main(String[] args) {
        System.out.println("Nested beans Profile Example");
        System.setProperty("spring.profiles.active", "prod");
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
                "nestedBeansWithProfileContext.xml");

        try {
            Object beanA = context.getBean("beanA");
            System.out.println(beanA);
            System.out.println("Contains devOnlyBean? " + context.containsBean("devOnlyBean"));
        } finally {
            context.close();
        }
                
        System.setProperty("spring.profiles.active", "dev");
        System.out.println("Profile is set to dev, now spring should override the 'beanA' bean");
        context = new ClassPathXmlApplicationContext(
                "nestedBeansWithProfileContext.xml");
        try {
            Object beanA = context.getBean("beanA");
            System.out.println(beanA);
            
            System.out.println("devOnlyBean is available only for dev profile");
            System.out.println("Contains devOnlyBean? " + context.containsBean("devOnlyBean"));
        } finally {
            context.close();
        }
        
    }

}

Output:

Nested beans Profile Example
Actual bean object
Contains devOnlyBean? false
Profile is set to dev, now spring should override the 'beanA' bean
Simple string for dev
devOnlyBean is available only for dev profile
Contains devOnlyBean? true

Download the source code

This was an example about spring nested beans. You can download the source code here: springNestedBeansExample.zip

Share.

Comments are closed.