Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Understanding the Lifecycle Phases of a Spring Bean

Tech 1

A Spring bean managed by an IoC container passes through distinct lifecycle stages. When configured via XML with initialization and destruction callbacks, the sequence is as follows:

  1. Bean Enstantiation: The container creates an instance by calling the constructor of the bean class.
  2. Property Injection: Dependencies are injected, typically through setter methods.
  3. Initialization Callbakc: If the init-method attribute is specified, the designated method is invoked after properties are set.
  4. Ready for Use: The bean is fully initialized and can be retrieved via getBean().
  5. Destruction Callback: When the container is shut down (e.g., close() is called on a ClassPathXmlApplicationContext), the destroy-method is executed.

The following example demonstrates this flow.

Employee class with lifecycle callbacks:

package com.example.model;

public class Employee {
    private int id;
    private String fullName;
    private String email;

    public Employee() {
        System.out.println("Step 1: Employee instance constructed");
    }

    public void setId(int id) {
        this.id = id;
    }

    public void setFullName(String fullName) {
        System.out.println("Step 2: Injecting fullName property");
        this.fullName = fullName;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public void startup() {
        System.out.println("Step 3: Initialization callback - startup()");
    }

    public void cleanup() {
        System.out.println("Step 5: Destruction callback - cleanup()");
    }

    @Override
    public String toString() {
        return "Employee{id=" + id + ", fullName='" + fullName + "', email='" + email + "'}";
    }
}

XML configuration (app-config.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="emp" class="com.example.model.Employee"
          init-method="startup" destroy-method="cleanup">
        <property name="fullName" value="Alice"/>
        <property name="id" value="101"/>
    </bean>

</beans>

Test driver:

package com.example.test;

import com.example.model.Employee;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class LifecycleDemo {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx =
                new ClassPathXmlApplicationContext("app-config.xml");
        Employee emp = ctx.getBean("emp", Employee.class);
        System.out.println("Step 4: Retrieved bean - " + emp);
        ctx.close();
    }
}

Enhancing the Lifecycle with a BeanPostProcessor

To inject custom logic around initialization, implement the BeanPostProcessor interface. The container then adds two additional callback points:

  • postProcessBeforeInitialization(Object bean, String beanName) – called before the init-method (or afterPropertiesSet).
  • postProcessAfterInitialization(Object bean, String beanName) – called after the initialization.

With a registered BeanPostProcessor, the full lifecycle sequence expands to:

  1. Constructor execution
  2. Setter-based property injection
  3. postProcessBeforeInitialization
  4. init-method invocation
  5. postProcessAfterInitialization
  6. Bean ready for use (retrieved via getBean())
  7. Destruction callback (destroy-method)

Example processor:

package com.example.processor;

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

public class MonitoringProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Before init for bean: " + beanName);
        return bean; // must return the bean instance
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("After init for bean: " + beanName);
        return bean;
    }
}

Register it in the same XML file as a normal bean:

<bean id="monitor" class="com.example.processor.MonitoringProcessor"/>

Key points about BeanPostProcessor:

  • Both interface methods must return the original bean instance (or a proxy if needed); returning null will lead to getBean() failing to resolve the target.
  • An ApplicationContext automatically detects BeanPostProcessor implementations defined in the configuration and applies them to every bean. When working directly with a BeanFactory, explicit registration via ConfigurableBeanFactory.addBeanPostProcessor(..) is required.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.