Comprehensive Overview of Spring Bean Lifecycle Extension Points
This article explains the Spring container's refresh process, details every extensible hook in a bean's lifecycle—from ApplicationContextInitializer to DisposableBean—provides usage scenarios, and includes complete Java code snippets illustrating how to implement each extension point for custom middleware and application development.
Spring's core concept is a container; when the container refreshes, the external view appears calm while the internal processes are highly dynamic. Spring Boot further simplifies configuration by following convention‑over‑configuration and providing auto‑configuration.
1. Background
The author enjoys Spring's auto‑configuration and uses it to create middleware and common utilities that require minimal effort from users. Understanding bean construction, lifecycle, and extension interfaces is essential for writing clean, extensible code.
2. Extension‑Point Invocation Order Diagram
The following diagram (originally an image) shows the sequential order of all extensible points during a bean's lifecycle.
3. ApplicationContextInitializer
Interface: org.springframework.context.ApplicationContextInitializer . It is invoked before the container refreshes, allowing users to perform actions before any beans are instantiated.
public class TestApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("[ApplicationContextInitializer]");
}
}Typical use cases include activating early configurations or performing dynamic byte‑code injection before class loading.
4. BeanDefinitionRegistryPostProcessor
Interface: org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor . Executed after reading bean definitions, it provides a hook to add or modify bean definitions before bean instantiation.
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 instances are created, allowing modification of bean 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‑population phases.
postProcessBeforeInstantiation : before the bean is instantiated.
postProcessAfterInstantiation : after the bean is instantiated.
postProcessPropertyValues : during property injection (e.g., handling @Autowired ).
postProcessBeforeInitialization and postProcessAfterInitialization remain from BeanPostProcessor .
public class TestInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
@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;
}
@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;
}
}7. SmartInstantiationAwareBeanPostProcessor
Provides three additional callbacks: predictBeanType , determineCandidateConstructors , and getEarlyBeanReference , useful for type prediction, constructor selection, and early reference exposure during circular dependencies.
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, giving access to the owning BeanFactory for custom logic or caching.
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 callbacks, it internally triggers six Aware interfaces (EnvironmentAware, EmbeddedValueResolverAware, ResourceLoaderAware, ApplicationEventPublisherAware, MessageSourceAware, ApplicationContextAware) after bean instantiation and before initialization.
10. BeanNameAware
Provides setBeanName(String name) before bean initialization, allowing modification or logging of 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
Marks a method to be invoked after dependency injection but before InitializingBean.afterPropertiesSet . It is not a Spring‑specific extension point but a standard Java annotation.
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 . The afterPropertiesSet() method runs after all bean properties are set and before the bean is made available.
public class NormalBeanA implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception { System.out.println("[InitializingBean] NormalBeanA"); }
}13. FactoryBean
Allows custom creation logic for complex beans. Implementations can control object creation, type, and singleton status.
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
Callback afterSingletonsInstantiated() runs 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
Single method run(String... args) is executed after the Spring Boot application starts, useful for post‑startup tasks. Multiple runners can be ordered with @Order .
public class TestCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception { System.out.println("[TestCommandLineRunner]"); }
}16. DisposableBean
Provides destroy() which is called when the bean is being destroyed, typically during application shutdown.
public class NormalBeanA implements DisposableBean {
@Override
public void destroy() throws Exception { System.out.println("[DisposableBean] NormalBeanA"); }
}17. ApplicationListener
Allows listening to Spring events such as ContextRefreshedEvent , ContextStartedEvent , ContextStoppedEvent , ContextClosedEvent , and RequestHandledEvent . Implementations can react to lifecycle changes or custom events.
18. Conclusion
By understanding and leveraging these Spring and Spring Boot extension points, developers can insert custom logic at virtually any stage of the bean lifecycle, enabling sophisticated middleware, automatic configuration, and fine‑grained control over application behavior.
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.
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.