Backend Development 16 min read

Comprehensive Overview of Spring and Spring Boot Extension Points and Bean Lifecycle

This article provides a detailed walkthrough of Spring and Spring Boot's core concepts, explains the bean container refresh process, and enumerates all major extension interfaces—including ApplicationContextInitializer, BeanDefinitionRegistryPostProcessor, BeanFactoryPostProcessor, InstantiationAwareBeanPostProcessor, SmartInstantiationAwareBeanPostProcessor, various *Aware interfaces, @PostConstruct, InitializingBean, FactoryBean, SmartInitializingSingleton, CommandLineRunner, DisposableBean, and ApplicationListener—accompanied by code samples and usage scenarios.

Top Architecture Tech Stack
Top Architecture Tech Stack
Top Architecture Tech Stack
Comprehensive Overview of Spring and Spring Boot Extension Points and Bean Lifecycle

Spring's core idea is a container that, when refreshed, appears calm externally while internally undergoing complex processing; Spring Boot further simplifies configuration through convention‑over‑configuration and auto‑wiring.

The article lists and explains the most important extension points that allow developers to hook into the bean lifecycle, providing both the interface signatures and typical use cases.

1. ApplicationContextInitializer

This callback runs before the container refresh, allowing custom initialization logic.

public class TestApplicationContextInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("[ApplicationContextInitializer]");
    }
}

Three ways to register it:

programmatically via springApplication.addInitializers(new TestApplicationContextInitializer())

via property context.initializer.classes=com.example.demo.TestApplicationContextInitializer

through Spring SPI in spring.factories

2. BeanDefinitionRegistryPostProcessor

public class TestBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        System.out.println("[BeanDefinitionRegistryPostProcessor] postProcessBeanDefinitionRegistry");
    }
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("[BeanDefinitionRegistryPostProcessor] postProcessBeanFactory");
    }
}

Used to modify bean definitions before they are turned into bean instances.

3. BeanFactoryPostProcessor

public class TestBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("[BeanFactoryPostProcessor]");
    }
}

Runs after bean definitions are loaded but before any bean is instantiated, allowing modification of bean metadata.

4. InstantiationAwareBeanPostProcessor

public class TestInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("[TestInstantiationAwareBeanPostProcessor] before initialization " + beanName);
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("[TestInstantiationAwareBeanPostProcessor] after initialization " + beanName);
        return bean;
    }
    @Override
    public Object postProcessBeforeInstantiation(Class
beanClass, String beanName) throws BeansException {
        System.out.println("[TestInstantiationAwareBeanPostProcessor] before instantiation " + beanName);
        return null;
    }
    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        System.out.println("[TestInstantiationAwareBeanPostProcessor] after instantiation " + beanName);
        return true;
    }
    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
        System.out.println("[TestInstantiationAwareBeanPostProcessor] postProcessPropertyValues " + beanName);
        return pvs;
    }
}

Extends BeanPostProcessor with callbacks before instantiation, after instantiation, and during property population.

5. SmartInstantiationAwareBeanPostProcessor

public class TestSmartInstantiationAwareBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {
    @Override
    public Class
predictBeanType(Class
beanClass, String beanName) throws BeansException {
        System.out.println("[TestSmartInstantiationAwareBeanPostProcessor] predictBeanType " + beanName);
        return beanClass;
    }
    @Override
    public Constructor
[] determineCandidateConstructors(Class
beanClass, String beanName) throws BeansException {
        System.out.println("[TestSmartInstantiationAwareBeanPostProcessor] determineCandidateConstructors " + beanName);
        return null;
    }
    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        System.out.println("[TestSmartInstantiationAwareBeanPostProcessor] getEarlyBeanReference " + beanName);
        return bean;
    }
}

Provides hooks for bean type prediction, constructor selection, and early reference exposure (useful for circular dependencies).

6. *Aware Interfaces (BeanFactoryAware, ApplicationContextAware, etc.)

These interfaces let a bean obtain internal Spring infrastructure objects such as BeanFactory, ApplicationContext, ResourceLoader, Environment, MessageSource, ApplicationEventPublisher, and its own bean name. Implementations typically store the injected reference for later use.

7. @PostConstruct

Marks a method to be invoked after dependency injection but before any custom init methods; it runs between postProcessBeforeInitialization and InitializingBean.afterPropertiesSet .

8. InitializingBean

public class NormalBeanA implements InitializingBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("[InitializingBean] NormalBeanA");
    }
}

Provides a single afterPropertiesSet callback for bean initialization.

9. FactoryBean

public class TestFactoryBean implements FactoryBean
{
    @Override
    public TestFactoryInnerBean getObject() throws Exception {
        System.out.println("[FactoryBean] getObject");
        return new TestFactoryInnerBean();
    }
    @Override
    public Class
getObjectType() { return TestFactoryInnerBean.class; }
    @Override
    public boolean isSingleton() { return true; }
    public static class TestFactoryInnerBean {}
}

Allows custom bean creation logic, often used for complex objects or proxies.

10. SmartInitializingSingleton

public class TestSmartInitializingSingleton implements SmartInitializingSingleton {
    @Override
    public void afterSingletonsInstantiated() {
        System.out.println("[TestSmartInitializingSingleton]");
    }
}

Called after all non‑lazy singleton beans have been instantiated.

11. CommandLineRunner

public class TestCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("[TestCommandLineRunner]");
    }
}

Executes after the application context is fully started; multiple runners can be ordered with @Order .

12. DisposableBean

public class NormalBeanA implements DisposableBean {
    @Override
    public void destroy() throws Exception {
        System.out.println("[DisposableBean] NormalBeanA");
    }
}

Invoked when the bean is destroyed, typically during application shutdown.

13. ApplicationListener

Allows beans to listen for Spring events such as ContextRefreshedEvent, ContextStartedEvent, ContextStoppedEvent, ContextClosedEvent, and RequestHandledEvent, enabling custom reactions at various lifecycle stages.

By understanding and leveraging these extension points, developers can customize the Spring container behavior, implement middleware, perform early initialization, and fine‑tune bean processing to suit complex business requirements.

backendJavaSpringSpringBootDependencyInjectionExtensionPointsBeanLifecycle
Top Architecture Tech Stack
Written by

Top Architecture Tech Stack

Sharing Java and Python tech insights, with occasional practical development tool tips.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.