Mastering Spring & Spring Boot: All Extension Points Explained

This article provides a comprehensive overview of Spring and Spring Boot's extension points, detailing each lifecycle hook, its purpose, typical use cases, and sample implementations, enabling developers to customize bean initialization, configuration, and shutdown processes with concrete code examples.

Programmer DD
Programmer DD
Programmer DD
Mastering Spring & Spring Boot: All Extension Points Explained

1. Background

Spring's core concept is a container; when the container refreshes, the outside appears calm while the inside undergoes intense activity. Spring Boot further encapsulates Spring, follows convention over configuration, and adds auto‑configuration, allowing almost zero‑config functionality by simply adding a dependency.

The author appreciates this auto‑configuration mechanism and applies it when developing middleware and common libraries, aiming to let users integrate with minimal effort. Understanding Spring's bean construction lifecycle and its extension interfaces is essential for writing cleaner, more powerful code.

Existing online resources rarely cover all extension points, so this article consolidates nearly all Spring & Spring Boot extension interfaces, their usage scenarios, and presents a complete call‑order diagram of bean loading within the container.

2. Extension Point Call Order Diagram

The following diagram illustrates the sequence of all extensible points during a bean's lifecycle.

3. ApplicationContextInitializer

Interface: org.springframework.context.ApplicationContextInitializer. It is invoked before the ConfigurableApplicationContext refresh, allowing users to perform actions prior to container initialization.

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

Registration methods:

Programmatically via

springApplication.addInitializers(new TestApplicationContextInitializer())

Configuration property

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

Spring SPI entry in

spring.factories

4. BeanDefinitionRegistryPostProcessor

Interface:

org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor

. Executed after bean definitions are read, allowing dynamic registration of additional bean definitions.

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

5. BeanFactoryPostProcessor

Interface:

org.springframework.beans.factory.config.BeanFactoryPostProcessor

. Called after bean definitions are loaded but before any bean instantiation, enabling modification of bean definition metadata.

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

6. InstantiationAwareBeanPostProcessor

Extends BeanPostProcessor and adds three methods, covering the instantiation and property injection phases. postProcessBeforeInstantiation: before bean is instantiated. postProcessAfterInstantiation: after bean is instantiated. postProcessPropertyValues: during property injection (e.g., @Autowired, @Resource). postProcessBeforeInitialization and postProcessAfterInitialization: same as standard BeanPostProcessor.

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

7. SmartInstantiationAwareBeanPostProcessor

Interface:

org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor

. Adds three additional hooks: predictBeanType: predicts bean type before postProcessBeforeInstantiation. determineCandidateConstructors: selects constructors after postProcessBeforeInstantiation. getEarlyBeanReference: provides early reference for circular dependencies after postProcessAfterInstantiation.

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

Interface: org.springframework.beans.factory.BeanFactoryAware. Called after bean instantiation but before property injection, allowing access to the owning BeanFactory.

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

Class:

org.springframework.context.support.ApplicationContextAwareProcessor

. Internally provides six Aware extension points (EnvironmentAware, EmbeddedValueResolverAware, ResourceLoaderAware, ApplicationEventPublisherAware, MessageSourceAware, ApplicationContextAware) that are invoked after bean instantiation and before property injection.

10. BeanNameAware

Interface: org.springframework.beans.factory.BeanNameAware. Triggered before bean initialization; the single method setBeanName provides the bean's registered name.

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

Annotation: javax.annotation.PostConstruct. Methods annotated with it are invoked after property injection ( postProcessBeforeInitialization) and before InitializingBean.afterPropertiesSet.

public class NormalBeanA {
    public NormalBeanA() {
        System.out.println("NormalBean constructor");
    }

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

12. InitializingBean

Interface: org.springframework.beans.factory.InitializingBean. Provides afterPropertiesSet, called after all bean properties are set and before postProcessAfterInitialization.

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

13. FactoryBean

Interface: org.springframework.beans.factory.FactoryBean. Allows custom bean creation logic, often used for complex instantiation or proxying.

public class TestFactoryBean implements FactoryBean<TestFactoryBean.TestFactoryInnerBean> {
    @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

Interface: org.springframework.beans.factory.SmartInitializingSingleton. Method afterSingletonsInstantiated is called after all non‑lazy singleton beans have been fully initialized.

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

15. CommandLineRunner

Interface: org.springframework.boot.CommandLineRunner. Single method run(String... args) executes after the application context has started.

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

16. DisposableBean

Interface: org.springframework.beans.factory.DisposableBean. Method destroy() is invoked when the bean is being destroyed, e.g., during context shutdown.

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

17. ApplicationListener

Interface: org.springframework.context.ApplicationListener. Allows listening to Spring events such as ContextRefreshedEvent, ContextStartedEvent, ContextStoppedEvent, ContextClosedEvent, and RequestHandledEvent, enabling custom behavior at various lifecycle stages.

18. Conclusion

By understanding and leveraging these Spring and Spring Boot extension points, developers can intervene at precise moments of the bean lifecycle to implement custom initialization, configuration, or cleanup logic, thereby extending the framework to suit specific business or middleware requirements.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Backend DevelopmentSpring BootExtension Pointsbean-lifecycle
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

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.