Backend Development 10 min read

SpringBoot Extension Interfaces: BeanPostProcessor, BeanFactoryPostProcessor

This article introduces the key SpringBoot extension interfaces—including BeanPostProcessor, BeanFactoryPostProcessor, BeanDefinitionRegistryPostProcessor, SmartInstantiationAwareBeanPostProcessor, SmartInitializingSingleton, ApplicationContextInitializer, EnvironmentPostProcessor, and the *Runner interfaces—explaining their purposes, usage patterns, and providing concrete code examples to help developers customize and extend SpringBoot applications effectively.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
SpringBoot Extension Interfaces: BeanPostProcessor, BeanFactoryPostProcessor

Environment: SpringBoot 2.7.16

1. Introduction

In SpringBoot project development, extension interfaces play a crucial role. They are essential for implementing functional extensions, improving flexibility, and facilitating maintenance. Mastering these interfaces helps developers increase development efficiency and project quality.

2. Extension Interfaces

2.1 BeanPostProcessor

This interface processes beans before and after initialization. Implementing it allows enhancement of all or specific beans, such as modifying bean instances or creating proxies.

<code>@Component
public class PackBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化之前处理逻辑...");
        // TODO
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化之后处理逻辑...");
        // TODO
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}
</code>

Both the before and after methods can manipulate the bean being created.

2.2 BeanFactoryPostProcessor

This interface allows custom modification of BeanDefinition objects before any beans are instantiated.

<code>@Component
public class PackBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition bd = beanFactory.getBeanDefinition("userService");
        bd.getPropertyValues().addPropertyValue("name", "Pack");
    }
}
</code>

It runs after the container starts but before bean instantiation, enabling changes such as setting a property value on UserService.

2.3 BeanDefinitionRegistryPostProcessor

Extends BeanFactoryPostProcessor and can also register new bean definitions dynamically.

<code>@Component
public class PackBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // modify existing beans
    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        BeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(UserService.class).getBeanDefinition();
        registry.registerBeanDefinition("userService", beanDefinition);
    }
}
</code>

The postProcessBeanDefinitionRegistry method can register beans based on conditions.

2.4 SmartInstantiationAwareBeanPostProcessor

This interface extends InstantiationAwareBeanPostProcessor and provides early bean reference exposure, pre‑instantiation, post‑instantiation, property population, and early reference methods.

<code>@Component
public class PackSmartInstantiationAwareBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        // create proxy before actual bean instance
        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        // return false to skip property population
        return true;
    }

    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        // modify property values
        return null;
    }

    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        // expose early reference
        return bean;
    }
}
</code>

2.5 SmartInitializingSingleton

Executed after all singleton beans have been instantiated, allowing custom logic that depends on the fully initialized container.

<code>@Component
public class PackSmartInitializingSingleton implements SmartInitializingSingleton {
    @Resource
    private ApplicationContext context;

    @Override
    public void afterSingletonsInstantiated() {
        // custom logic, e.g., obtain a bean and invoke init()
        UserService us = context.getBean(UserService.class);
        us.init();
    }
}
</code>

2.6 ApplicationContextInitializer

Runs before the Spring container refresh, enabling custom configuration of the ApplicationContext.

<code>public class PackApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableWebApplicationContext> {
    @Override
    public void initialize(ConfigurableWebApplicationContext applicationContext) {
        // custom configuration logic
    }
}
</code>

It must be registered via the Spring factories file, not with @Component:

<code>org.springframework.context.ApplicationContextInitializer=\
com.pack.expansions.PackApplicationContextInitializer
</code>

2.7 EnvironmentPostProcessor

Allows early manipulation of the SpringBoot environment, such as loading additional property sources.

<code>public class PackEnvironmentPostProcessor implements EnvironmentPostProcessor {
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        YamlPropertySourceLoader sourceLoader = new YamlPropertySourceLoader();
        try {
            List<PropertySource<?>> list = sourceLoader.load("pack", new ClassPathResource("com/pack/binder/properties/pack.yml"));
            list.forEach(propertySource -> environment.getPropertySources().addLast(propertySource));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
</code>

Registration is also done via the factories file:

<code>org.springframework.boot.env.EnvironmentPostProcessor=\
com.pack.binder.properties.PackEnvironmentPostProcessor
</code>

2.8 *Runner Interfaces

SpringBoot provides ApplicationRunner and CommandLineRunner, which are invoked after the application has started.

<code>@Component
public class PackApplicationRunner implements ApplicationRunner, CommandLineRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        // ApplicationRunner callback
    }

    @Override
    public void run(String... args) throws Exception {
        // CommandLineRunner callback
    }
}
</code>

These interfaces enable execution of custom logic at the very end of the startup process.

Javabackend developmentSpringBootBeanPostProcessorExtension Interfaces
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

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