Understanding Bean Scopes and Lifecycle in Spring Framework

Bean Scopes in Spring

When the Spring IoC container starts, it reads bean definitions from configuration files (e.g., XML) and converts each <bean> element into a BeanDefinition object. The scope attribute within BeanDefinition determines the bean's scope. Spring provides five built-in scopes, three of which are only available in a web-aware ApplicationContext. Developers can also define custom scopes.

Scope Description
singleton (Default) A single instance per Spring IoC container.
prototype Any number of object instances per bean definition.
request One bean instance per HTTP request. Only available in web-aware Spring ApplicationContext.
session One bean instance per HTTP session. Available only in web-aware ApplicationContext.
globalSession One bean instance per global HTTP session (typically used in portlet environments). Only available in web-aware ApplicationContext.

To demonstrate the difference between singleton and prototype, consider a simple class XMLInstance. The following snippet retrieves the bean twice via BeanFactory.getBean():

XMLInstance instance = (XMLInstance) factory.getBean("xmlinstance");
instance.setName("123");
instance.Breath();
instance = (XMLInstance) factory.getBean("xmlinstance");
instance.Breath();

With the default singleton scope (configuration below), both calls return the same object instance:

<bean id="xmlinstance" class="com.demo.model.XMLInstance" scope="singleton">
    <property name="air" ref="CleanAir"/>
    <property name="name" value="abc"/>
</bean>

Output:

Name:123;Air:CleanAir
Name:123;Air:CleanAir

If the scope is changed to prototype, each getBean() call creates a new instance:

<bean id="xmlinstance" class="com.demo.model.XMLInstance" scope="prototype">
    <property name="air" ref="CleanAir"/>
    <property name="name" value="abc"/>
</bean>

Output:

Name:123;Air:CleanAir
Name:abc;Air:CleanAir

Bean Lifecycle in Spring

Understanding the lifecycle of a bean helps clarify how Spring creates, configures, and destroys beans. The following steps outline the full lifecycle within an ApplicationContext:

  1. The container instantiates beans (singleton, non-lazy by default).
  2. Properties and dependencies are injected as per bean definition.
  3. If the bean implements BeanNameAware, Spring calls setBeanName() with the bean's id.
  4. If the bean implements BeanFactoryAware, Spring calls setBeanFactory() with the owning factory.
  5. If the bean implements ApplicationContextAware, Spring calls setApplicationContext() with the context.
  6. Any registered BeanPostProcessor beans are invoked with postProcessBeforeInitialization().
  7. If the bean implements InitializingBean, Spring calls afterPropertiesSet().
  8. If a custom init-method is defined, its invoked.
  9. Any registered BeanPostProcessor beans are invoked with postProcessAfterInitialization().
  10. The bean is now ready for use. Singleton beans are cached; prototype beans are handed over to the caller and not managed by the container thereafter.
  11. When the container shuts down, if the bean implements DisposableBean, Spring calls destroy().
  12. If a custom destroy-method is defined, it is invoked.

The following example demonstrates the lifecycle methods using a custom bean UserBean which implements several aware interfaces and lifecycle interfaces.

package com.demo.model;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class UserBean implements BeanNameAware, BeanFactoryAware, 
        InitializingBean, DisposableBean, ApplicationContextAware {

    private String userName;

    public UserBean() {
        System.out.println("Constructor of UserBean");
    }

    public void setUserName(String userName) {
        this.userName = userName;
        System.out.println("Setter called");
    }

    public String getUserName() {
        return userName;
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("BeanNameAware.setBeanName()");
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("BeanFactoryAware.setBeanFactory()");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("ApplicationContextAware.setApplicationContext()");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean.afterPropertiesSet()");
    }

    // Custom init method
    public void customInit() {
        System.out.println("Custom init-method called");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("DisposableBean.destroy()");
    }

    // Custom destroy method
    public void customDestroy() {
        System.out.println("Custom destroy-method called");
    }
}

Additionally, a custom BeanPostProcessor implementation is defined:

package com.demo.model;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class CustomBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("PostProcessBeforeInitialization");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("PostProcessAfterInitialization");
        return bean;
    }
}

The XML configuration registers the bean with custom init and destroy methods, and also registers the post-processor:

<bean id="user" class="com.demo.model.UserBean" init-method="customInit" destroy-method="customDestroy">
    <property name="userName" value="abc"/>
</bean>

<bean id="customPostProcessor" class="com.demo.model.CustomBeanPostProcessor"/>

Finally, a test program loads the context, retrieves the bean, and closes the context:

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserBean user = context.getBean("user", UserBean.class);
((ClassPathXmlApplicationContext) context).close();

The output shows the order of lifecycle callbacks:

Constructor of UserBean
Setter called
BeanNameAware.setBeanName()
BeanFactoryAware.setBeanFactory()
ApplicationContextAware.setApplicationContext()
PostProcessBeforeInitialization
InitializingBean.afterPropertiesSet()
Custom init-method called
PostProcessAfterInitialization
DisposableBean.destroy()
Custom destroy-method called

Tags: Spring Bean Scope singleton Prototype Bean Lifecycle

Posted on Fri, 22 May 2026 18:47:37 +0000 by richrock