Backend Development 17 min read

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

This article provides a detailed exploration of Spring and Spring Boot's extensible interfaces, illustrating the complete bean lifecycle, presenting an invocation order diagram, and offering practical code examples for each extension point such as ApplicationContextInitializer, BeanFactoryPostProcessor, SmartInstantiationAwareBeanPostProcessor, and more, enabling developers to customize container behavior effectively.

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

1. Background: Spring's core concept is a container; when the container refreshes, internal complexity arises. Spring Boot builds on Spring, following convention over configuration and auto‑configuration, allowing almost zero‑configuration bean assembly.

I appreciate this auto‑configuration mechanism and use it when developing middleware and common libraries, aiming for minimal integration cost. Understanding the bean construction lifecycle and the various extension interfaces helps write cleaner code and leverage these points.

Existing articles rarely cover all extension points, so this article summarizes almost all Spring & Spring Boot extension interfaces and provides a diagram of the order in which they are invoked during bean loading.

2. Extension Interface Invocation Order Diagram

The following diagram (image) shows the complete call order of extensible points in the bean lifecycle.

3. ApplicationContextInitializer

Interface org.springframework.context.ApplicationContextInitializer is invoked before the container refresh, allowing users to perform actions before bean creation. Example implementation:

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

Three ways to register it: add via SpringApplication.addInitializers , configure context.initializer.classes in properties, or use Spring SPI by adding the entry to spring.factories .

4. BeanDefinitionRegistryPostProcessor

Interface org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor runs after bean definitions are read, allowing dynamic registration of bean definitions. Example:

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

Use cases include loading bean definitions from outside the classpath.

5. BeanFactoryPostProcessor

Interface org.springframework.beans.factory.config.BeanFactoryPostProcessor is called after bean definitions are loaded but before bean instantiation, allowing modification of bean definition metadata.

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

6. InstantiationAwareBeanPostProcessor

This interface extends BeanPostProcessor and adds three methods that operate during bean instantiation and property injection phases.

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

Useful for collecting beans of a certain type or setting properties uniformly.

7. SmartInstantiationAwareBeanPostProcessor

Provides three additional callbacks: predictBeanType , determineCandidateConstructors , and getEarlyBeanReference .

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

8. BeanFactoryAware

Allows a bean to obtain the owning BeanFactory after instantiation but before property population.

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

9. ApplicationContextAwareProcessor

Although it does not define its own extension points, it internally triggers six Aware interfaces (EnvironmentAware, EmbeddedValueResolverAware, ResourceLoaderAware, ApplicationEventPublisherAware, MessageSourceAware, ApplicationContextAware) after bean instantiation and before property injection.

10. BeanNameAware

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

11. @PostConstruct

Methods annotated with @PostConstruct are invoked after bean properties are set but before the bean is fully initialized.

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

12. InitializingBean

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

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

14. SmartInitializingSingleton

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

15. CommandLineRunner

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

16. DisposableBean

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

17. ApplicationListener

The org.springframework.context.ApplicationListener interface can listen to Spring events such as ContextRefreshedEvent , ContextStartedEvent , ContextStoppedEvent , ContextClosedEvent , and RequestHandledEvent , allowing custom behavior during various phases of the application lifecycle.

18. Conclusion

By examining these Spring and Spring Boot extension points, developers can gain insight into the bean lifecycle and strategically inject custom logic at various stages, enabling advanced initialization, resource handling, and middleware development.

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.