Backend Development 23 min read

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

This article provides a comprehensive overview of Spring and Spring Boot's extensible interfaces, detailing each extension point, its lifecycle stage, usage scenarios, and sample code, enabling developers to better understand and customize bean initialization within the backend framework.

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

Background

The core idea of Spring is the container; when the container refreshes, it appears smooth externally but is turbulent internally. Spring Boot further packages Spring, follows convention over configuration, and adds auto‑configuration. Often, adding a dependency results in near‑zero configuration to achieve a functional setup.

I love this auto‑configuration mechanism, so I also use it when developing middleware and common utility libraries, allowing users to integrate with minimal effort. To master auto‑configuration, one must understand Spring's bean construction lifecycle and its various extension interfaces. Knowing the bean lifecycle also deepens understanding of Spring and enables writing cleaner code.

Searching online for Spring extension points yields few comprehensive articles; most only cover common points.

Therefore, in this article I summarize almost all Spring & Spring Boot extension interfaces, their usage scenarios, and present a diagram of the order in which all extensible points are invoked during a bean's lifecycle, revealing how a bean is gradually loaded into the Spring container.

Extensible Interface Startup Call Sequence Diagram

Below is the sequence of all extensible points within the Spring container during a bean's lifecycle; each will be analyzed in detail.

1. ApplicationContextInitializer

org.springframework.context.ApplicationContextInitializer

This callback interface is invoked before the Spring container refreshes the ConfigurableApplicationContext . In simple terms, its initialize method is called before the container refreshes, allowing users to perform custom actions before the container is initialized.

Typical scenarios include activating early configurations or performing dynamic byte‑code injection before classes are loaded.

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

Because the Spring container is not yet initialized, the extension can take effect via three ways:

In the main class, call springApplication.addInitializers(new TestApplicationContextInitializer()) .

Configure in properties: context.initializer.classes=com.example.demo.TestApplicationContextInitializer .

Add to spring.factories for SPI: org.springframework.context.ApplicationContextInitializer=com.example.demo.TestApplicationContextInitializer .

2. BeanDefinitionRegistryPostProcessor

org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor

This interface is executed after Spring reads the project's beanDefinition , providing an additional extension point.

Use case: dynamically register your own beanDefinition , allowing loading of beans outside the classpath.

Extension method:

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");
    }
}

3. BeanFactoryPostProcessor

org.springframework.beans.factory.config.BeanFactoryPostProcessor

This is an extension interface of the beanFactory , invoked after Spring reads beanDefinition information but before bean instantiation.

Users can modify the metadata of already registered beanDefinition objects.

Extension method:

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

4. InstantiationAwareBeanPostProcessor

org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor

This interface extends BeanPostProcessor . The difference is that it adds three methods, expanding the extension range to the instantiation and property‑injection phases.

The five main methods are:

postProcessBeforeInstantiation : before bean instantiation (before new ).

postProcessAfterInstantiation : after bean instantiation (after new ).

postProcessPropertyValues : during property injection, underlying the implementation of @Autowired , @Resource , etc.

postProcessBeforeInitialization : before bean initialization (before Spring injects the bean into the context).

postProcessAfterInitialization : after bean initialization (after the bean is injected into the context).

Use case: collect beans of a certain type throughout their lifecycle or uniformly set properties.

Extension method:

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;
    }
}

5. SmartInstantiationAwareBeanPostProcessor

org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor

This interface adds three trigger methods:

predictBeanType : before postProcessBeforeInstantiation , predicts the bean type for early type lookup.

determineCandidateConstructors : after postProcessBeforeInstantiation , determines which constructors may be used for instantiation.

getEarlyBeanReference : after postProcessAfterInstantiation , used in circular‑dependency scenarios to expose an early reference.

Extension method:

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;
    }
}

6. BeanFactoryAware

org.springframework.beans.factory.BeanFactoryAware

This interface has a single trigger point after bean instantiation but before property injection (before setter). The method setBeanFactory provides access to the BeanFactory .

Use case: obtain the BeanFactory after the bean is created to perform custom logic or cache it for later use.

Extension method:

public class TestBeanFactoryAware implements BeanFactoryAware {
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("[TestBeanFactoryAware] " + beanFactory.getBean(TestBeanFactoryAware.class).getClass().getSimpleName());
    }
}

7. ApplicationContextAwareProcessor

org.springframework.context.support.ApplicationContextAwareProcessor

Although this class itself has no direct extension points, it internally triggers six Aware interfaces after bean instantiation and before initialization, allowing beans to obtain the environment, resource loader, application event publisher, message source, and application context.

These six Aware interfaces are:

EnvironmentAware – obtain the Environment object.

EmbeddedValueResolverAware – obtain a StringValueResolver for resolving @Value placeholders.

ResourceLoaderAware – obtain a ResourceLoader to load classpath resources.

ApplicationEventPublisherAware – obtain an ApplicationEventPublisher to publish events.

MessageSourceAware – obtain a MessageSource for internationalization.

ApplicationContextAware – obtain the ApplicationContext itself, which also implements several other Aware interfaces.

8. BeanNameAware

org.springframework.beans.factory.BeanNameAware

This Aware interface has a single trigger before bean initialization: setBeanName . It allows the bean to know its registered name in the container.

Use case: modify or record the bean name before initialization.

Extension method:

public class NormalBeanA implements BeanNameAware {
    public NormalBeanA() {
        System.out.println("NormalBean constructor");
    }
    @Override
    public void setBeanName(String name) {
        System.out.println("[BeanNameAware] " + name);
    }
}

9. @PostConstruct

javax.annotation.PostConstruct

This annotation marks a method to be called after bean property injection but before InitializingBean.afterPropertiesSet . It is not a Spring‑specific extension point but follows the same lifecycle timing.

Use case: initialize certain properties after dependencies are injected.

Extension method:

public class NormalBeanA {
    public NormalBeanA() {
        System.out.println("NormalBean constructor");
    }
    @PostConstruct
    public void init() {
        System.out.println("[PostConstruct] NormalBeanA");
    }
}

10. InitializingBean

org.springframework.beans.factory.InitializingBean

This interface provides a single initialization method afterPropertiesSet , invoked before postProcessAfterInitialization . It is used for custom initialization logic after all bean properties have been set.

Use case: perform startup tasks such as initializing metrics.

Extension method:

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

11. FactoryBean

org.springframework.beans.factory.FactoryBean

When bean instantiation is complex, implementing FactoryBean allows custom creation logic. Spring itself provides many FactoryBean implementations. Since Spring 3.0, FactoryBean supports generics.

Use case: create a proxy bean that logs method calls.

Extension method:

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 {}
}

12. SmartInitializingSingleton

org.springframework.beans.factory.SmartInitializingSingleton

This interface defines afterSingletonsInstantiated , called after all non‑lazy singleton beans have been fully initialized (after postProcessAfterInitialization ).

Use case: perform post‑processing once the entire singleton graph is ready.

Extension method:

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

13. CommandLineRunner

org.springframework.boot.CommandLineRunner

This interface has a single method run(String... args) , executed after the application has started. Multiple runners can be ordered with @Order .

Use case: execute pre‑processing logic after the application boots.

Extension method:

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

14. DisposableBean

org.springframework.beans.factory.DisposableBean

This interface defines destroy() , called when the bean is being destroyed, e.g., when applicationContext.registerShutdownHook() is invoked.

Use case: release resources or perform cleanup.

Extension method:

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

15. ApplicationListener

org.springframework.context.ApplicationListener

Although not a core Spring Boot extension point, ApplicationListener can listen to framework events (e.g., ContextRefreshedEvent, ContextStartedEvent, ContextStoppedEvent, ContextClosedEvent, RequestHandledEvent) and custom events, allowing developers to react at various stages of the application lifecycle.

ContextRefreshedEvent – published when the ApplicationContext is initialized or refreshed.

ContextStartedEvent – published when ConfigurableApplicationContext.start() is called.

ContextStoppedEvent – published when ConfigurableApplicationContext.stop() is called.

ContextClosedEvent – published when ConfigurableApplicationContext.close() is called.

RequestHandledEvent – web‑specific event indicating an HTTP request has been processed.

Conclusion

From these Spring & Spring Boot extension points we can glimpse the entire bean lifecycle. When developing business logic or middleware, we can wisely leverage these hooks at various startup stages to achieve custom initialization and enhance code elegance.

Previous Content

The project abandoned by Alibaba was revived! – 2024-12-24

SpringBoot + MinIO + kkfile to achieve file preview – 2024-12-23

Zhihu hot discussion: What level is the 12306 ticketing system globally? – 2024-12-22

Linux under the Windows 11 skin arrives, spectacular! – 2024-12-20

Remember these 16 SpringBoot extension interfaces to write cleaner code – 2024-12-19

— EOF —

JavaBackend DevelopmentSpringSpring BootExtension Pointsbean lifecycle
Architecture Digest
Written by

Architecture Digest

Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.

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.