Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Understanding the Instantiation and Initialization Phases of Spring Bean Lifecycle

Tech 1

Spring Bean Lifecycle Overview

A Bean in the Spring framwork undergoes a multi-stage lifecycle from creation to destruction. The following diagram illustrates the core stages of a Bean's lifecycle:

During these lifecycle phases, Spring provides corresponding interfaces and annotations, allowing developers to perform custom operations at specific points.

Practical Implementation Examples

Creating a Spring Context Utility

A common application of lifecycle interfaces is building a utility class to access the Spring ApplicationContext. This class is annotated with @Component to register it as a Bean, and it implements the ApplicationContextAware interface. When this Bean is created and initialized, Spring will invoke the setApplicationContext method, providing the context.

@Component
public class ContextHolder implements ApplicationContextAware {
    private static ApplicationContext ctx;

    @Override
    public void setApplicationContext(ApplicationContext ctx) throws BeansException {
        ContextHolder.ctx = ctx;
    }

    public static ApplicationContext getContext() {
        if (ctx == null) {
            throw new IllegalStateException("ApplicationContext is not available.");
        }
        return ctx;
    }

    public static Object getBean(String beanName) {
        return getContext().getBean(beanName);
    }

    public static <T> T getBean(String beanName, Class<T> beanClass) {
        return getContext().getBean(beanName, beanClass);
    }

    public static <T> T getBean(Class<T> beanClass) {
        return getContext().getBean(beanClass);
    }
}

Executing Post-Dependency Injection Initialization

When a Bean requires initialization after its dependencies have been injected, the @PostConstruct annotation can be used. In this example, DataService needs to call a method on its injected DataLoader dependency to populate initial data.

@Service
public class DataService {
    private DataLoader loader;
    private List<String> cachedData;

    @Autowired
    public void setDataLoader(DataLoader loader) {
        this.loader = loader;
    }

    @PostConstruct
    public void loadCache() {
        this.cachedData = loader.fetchInitialData();
    }

    public void process() {
        // Business logic
    }
}

@Service
class DataLoader {
    public List<String> fetchInitialData() {
        // Implementation
        return new ArrayList<>();
    }
}

@PostConstruct is a standard JSR-250 annotation. Alternative initialization mechanisms include implementing the InitializingBean interface's afterPropertiesSet method, or specifying an initialization method via XML init-method configuraton or the @Bean annotation's initMethod attribute.

Core Bean Creation Process in Spring Source Code

Spring's internal bean creation process consists of three primary phases: Instantiation -> Property Population -> Initialization. The property population phase handles dependency injection (e.g., @Autowired). The initialization phase encompasses all steps from calling XXXAware.setXXX() methods to postProcessAfterInitialization().

The doCreateBean method in AbstractAutowireCapableBeanFactory demonstrates this structure:

protected Object doCreateBean(String beanName, RootBeanDefinition def, Object... args) {
    BeanWrapper wrapper = null;
    if (wrapper == null) {
        // 1. Instantiate
        wrapper = createBeanInstance(beanName, def, args);
    }
    Object beanInstance = wrapper.getWrappedInstance();

    // Early singleton exposure logic...

    Object exposedObject = beanInstance;
    try {
        // 2. Populate properties
        populateBean(beanName, def, wrapper);
        // 3. Initialize
        exposedObject = initializeBean(beanName, exposedObject, def);
    } catch (Throwable ex) {
        // Handle exception
    }
    return exposedObject;
}

The initializeBean() Method Breakdown

Within initializeBean(), execution proceeds in a specific order: invokeAwareMethods() -> applyBeanPostProcessorsBeforeInitialization() -> invokeInitMethods() -> applyBeanPostProcessorsAfterInitialization().

protected Object initializeBean(String name, Object bean, RootBeanDefinition def) {
    // Step 1: Call Aware methods
    invokeAwareMethods(name, bean);

    Object processedBean = bean;
    if (def == null || !def.isSynthetic()) {
        // Step 2: BeanPostProcessor.pre-init
        processedBean = applyBeanPostProcessorsBeforeInitialization(processedBean, name);
    }

    try {
        // Step 3: Call init methods
        invokeInitMethods(name, processedBean, def);
    } catch (Throwable ex) {
        throw new BeanCreationException(name, "Init method failed", ex);
    }

    if (def == null || !def.isSynthetic()) {
        // Step 4: BeanPostProcessor.post-init
        processedBean = applyBeanPostProcessorsAfterInitialization(processedBean, name);
    }
    return processedBean;
}

The invokeAwareMethods() Method

The invokeAwareMethods() method only handles the BeanNameAware, BeanClassLoaderAware, and BeanFactoryAware interfaces. Other Aware interfaces, such as ApplicationContextAware, are processed by specific BeanPostProcessor implementations like ApplicationContextAwareProcessor within its postProcessBeforeInitialization() method.

The applyBeanPostProcessorsBeforeInitialization() Method

This method iterates through all registered BeanPostProcessor beans and calls their postProcessBeforeInitialization() method.

public Object applyBeanPostProcessorsBeforeInitialization(Object bean, String name) throws BeansException {
    Object result = bean;
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        Object current = processor.postProcessBeforeInitialization(result, name);
        if (current == null) {
            return result;
        }
        result = current;
    }
    return result;
}

Spring's CommonAnnotationBeanPostProcessor (which also handles @Resource) is a BeanPostProcessor that processes the @PostConstruct annotation. Its constructor registers the annotation type, and its parent class InitDestroyAnnotationBeanPostProcessor implements the logic to find and invoke methods annotated with @PostConstruct in its postProcessBeforeInitialization().

The invokeInitMethods() Method

This method first checks if the bean implements InitializingBean and calls afterPropertiesSet(). It then checks the bean definition for any custom initialization method names and invokes them via reflection.

protected void invokeInitMethods(String name, Object bean, RootBeanDefinition def) throws Throwable {
    boolean isInitializingBean = (bean instanceof InitializingBean);
    if (isInitializingBean && (def == null || !def.isExternallyManagedInitMethod("afterPropertiesSet"))) {
        // Call afterPropertiesSet
        ((InitializingBean) bean).afterPropertiesSet();
    }

    if (def != null && bean.getClass() != NullBean.class) {
        String[] customInitMethods = def.getInitMethodNames();
        if (customInitMethods != null) {
            for (String methodName : customInitMethods) {
                if (isValidCustomMethod(isInitializingBean, methodName, def)) {
                    // Invoke custom init method via reflection
                    invokeCustomInitMethod(name, bean, def, methodName);
                }
            }
        }
    }
}

The applyBeanPostProcessorsAfterInitialization() Method

Similar to its "Before" counterpart, this method iterates through BeanPostProcessor instances and calls their postProcessAfterInitialization() method, allowing for further modification of the bean after initialization.

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.