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.

Shepherd Advanced Notes
Shepherd Advanced Notes
Shepherd Advanced Notes
Master 16 SpringBoot Extension Interfaces for Cleaner, More Elegant Code

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.TestApplicationContextInitializer

Spring SPI: add

org.springframework.context.ApplicationContextInitializer=com.example.demo.TestApplicationContextInitializer

to

spring.factories

2. BeanDefinitionRegistryPostProcessor

Interface:

org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor

Executed 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.BeanFactoryPostProcessor

Runs 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.SmartInstantiationAwareBeanPostProcessor

Provides 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.ApplicationContextAwareProcessor

Although 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&lt;T&gt; ).

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.

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.

backendSpringDependency Injectionextension-pointsBean Lifecyclespring-boot
Shepherd Advanced Notes
Written by

Shepherd Advanced Notes

Dedicated to sharing advanced Java technical insights, daily work snippets, and the power of persistent effort.

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.