Master 16 SpringBoot Extension Interfaces for Cleaner, More Elegant Code
This article enumerates and explains sixteen Spring and SpringBoot extension interfaces, illustrates their positions in the bean lifecycle with a call‑order diagram, and provides concrete usage scenarios and code samples so developers can harness these hooks to write more elegant and maintainable applications.
Background
Spring's core concept is the container; when the container refreshes, the outside appears calm while the inside undergoes complex processing. Spring Boot further wraps Spring, follows "convention over configuration" and auto‑configuration, allowing most functionality with minimal configuration.
The author enjoys auto‑configuration and uses it when building middleware or common libraries, aiming to let users integrate with minimal effort. Understanding the bean construction lifecycle and the various extension interfaces is essential for writing elegant code.
Extension Interface Call‑Order Diagram
The following diagram (shown in the original article) visualises the order in which all extension points are invoked during a bean's lifecycle inside the Spring container.
1. ApplicationContextInitializer
Interface: org.springframework.context.ApplicationContextInitializer Invoked before the Spring container refreshes, allowing users to execute logic prior to bean loading. Typical scenarios include activating early configurations or performing byte‑code injection before classes are loaded.
Extension methods:
public class TestApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("[ApplicationContextInitializer]");
}
}Three ways to register:
In the main class:
springApplication.addInitializers(new TestApplicationContextInitializer())Via configuration:
context.initializer.classes=com.example.demo.TestApplicationContextInitializerSpring SPI: add
org.springframework.context.ApplicationContextInitializer=com.example.demo.TestApplicationContextInitializerto
spring.factories2. BeanDefinitionRegistryPostProcessor
Interface:
org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessorExecuted after reading bean definitions, providing a hook to register additional bean definitions, e.g., dynamically loading beans outside the classpath.
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
Interface:
org.springframework.beans.factory.config.BeanFactoryPostProcessorRuns after bean definitions are read but before any bean is instantiated, allowing modification of bean definition metadata.
public class TestBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("[BeanFactoryPostProcessor]");
}
}4. InstantiationAwareBeanPostProcessor
Extends BeanPostProcessor and adds three extra callbacks covering the instantiation and property‑population phases. postProcessBeforeInstantiation: before new is called. postProcessAfterInstantiation: after the bean instance is created. postProcessPropertyValues: during property injection (e.g., @Autowired, @Resource). postProcessBeforeInitialization: before custom init methods. postProcessAfterInitialization: after custom init methods.
Typical use‑cases: collecting beans of a certain type throughout their lifecycle or applying uniform property settings.
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;
}
}5. SmartInstantiationAwareBeanPostProcessor
Interface:
org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessorProvides three additional callbacks: predictBeanType: predicts bean type before postProcessBeforeInstantiation. Used by BeanFactory.getType(name) when the type is unknown. determineCandidateConstructors: decides which constructors are eligible after postProcessBeforeInstantiation. getEarlyBeanReference: invoked after postProcessAfterInstantiation to expose a bean reference early (e.g., for circular‑dependency handling).
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
Interface: org.springframework.beans.factory.BeanFactoryAware Single callback setBeanFactory runs 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());
}
}7. ApplicationContextAwareProcessor
Class:
org.springframework.context.support.ApplicationContextAwareProcessorAlthough it does not define its own extension points, it internally triggers six Aware interfaces after bean instantiation and before initialization, such as EnvironmentAware, EmbeddedValueResolverAware, ResourceLoaderAware, ApplicationEventPublisherAware, MessageSourceAware, and ApplicationContextAware. These allow beans to obtain the environment, resource loader, event publisher, message source, and the application context itself.
8. BeanNameAware
Interface: org.springframework.beans.factory.BeanNameAware Single method setBeanName is called before
postProcessBeforeInitialization</>, enabling a bean to know its registered name and optionally modify it.</p>
<pre><code>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
Annotation: javax.annotation.PostConstruct Methods annotated with @PostConstruct are invoked after property injection but before InitializingBean.afterPropertiesSet , allowing custom initialization logic.
public class NormalBeanA {
public NormalBeanA() { System.out.println("NormalBean constructor"); }
@PostConstruct
public void init() { System.out.println("[PostConstruct] NormalBeanA"); }
}10. InitializingBean
Interface: org.springframework.beans.factory.InitializingBean Provides a single method afterPropertiesSet that runs after property injection and before postProcessAfterInitialization . Useful for initializing business metrics at startup.
public class NormalBeanA implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("[InitializingBean] NormalBeanA");
}
}11. FactoryBean
Interface: org.springframework.beans.factory.FactoryBean Allows custom creation logic for complex beans. Spring itself provides dozens of implementations. Since Spring 3.0 the interface supports generics ( FactoryBean<T> ).
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 {}
}12. SmartInitializingSingleton
Interface: org.springframework.beans.factory.SmartInitializingSingleton Single method afterSingletonsInstantiated runs after all non‑lazy singleton beans have been fully initialized (i.e., after postProcessAfterInitialization ).
public class TestSmartInitializingSingleton implements SmartInitializingSingleton {
@Override
public void afterSingletonsInstantiated() {
System.out.println("[TestSmartInitializingSingleton]");
}
}13. CommandLineRunner
Interface: org.springframework.boot.CommandLineRunner Method run(String... args) is invoked after the application has started. Multiple runners can be ordered with @Order .
public class TestCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("[TestCommandLineRunner]");
}
}14. DisposableBean
Interface: org.springframework.beans.factory.DisposableBean Single method destroy() is called when the bean is destroyed, e.g., during applicationContext.registerShutdownHook() .
public class NormalBeanA implements DisposableBean {
@Override
public void destroy() throws Exception {
System.out.println("[DisposableBean] NormalBeanA");
}
}15. ApplicationListener
Interface: org.springframework.context.ApplicationListener Allows listening to Spring events such as ContextRefreshedEvent , ContextStartedEvent , ContextStoppedEvent , ContextClosedEvent , and RequestHandledEvent . Custom listeners can be used to react to lifecycle milestones or business events.
Conclusion
By reviewing these Spring and SpringBoot extension points, we can trace the complete bean lifecycle and strategically insert custom logic at various stages, enabling sophisticated initialization, resource handling, and cleanup in both business code and middleware.
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.
Shepherd Advanced Notes
Dedicated to sharing advanced Java technical insights, daily work snippets, and the power of persistent effort.
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.
