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.
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.TestApplicationContextInitializerSpring SPI entry in
spring.factories4. 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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
