Unlock Spring BeanPostProcessor & BeanFactoryPostProcessor for Backend Customization
This article explains how to extend the Spring IoC container using custom BeanPostProcessor, BeanFactoryPostProcessor, and FactoryBean implementations, covering registration methods, ordering, interaction with AOP proxies, and practical code examples for tracing bean creation and externalizing configuration properties.
Environment: Spring 5.3.10
Typically, application developers do not subclass ApplicationContext implementations; instead, the Spring IoC container can be extended by registering implementations of special integration interfaces.
Customizing Beans with BeanPostProcessor
The BeanPostProcessor interface defines callback methods that you can implement to provide your own (or override the container's default) instantiation logic, dependency resolution logic, etc. If you want to execute custom logic after the Spring container has instantiated, configured, and initialized a bean, you can write one or more custom BeanPostProcessor implementations.
You can configure multiple BeanPostProcessor instances and control their execution order by setting the order property, which is effective only when the BeanPostProcessor implements the Ordered interface. When writing your own BeanPostProcessor, consider also implementing the ordered interface.
BeanPostProcessor instances operate on bean (or object) instances after the container creates them. The scope of a BeanPostProcessor is per container; it only processes beans within the container where it is defined. To modify the actual bean definition (the blueprint), you need to use a BeanFactoryPostProcessor.
The org.springframework.beans.factory.config.BeanPostProcessor interface consists of two callback methods. When registered as a post‑processor, it receives callbacks for each bean instance before the container’s initialization methods (such as InitializingBean.afterPropertiesSet() or any declared init method) are invoked, and after any bean initialization callbacks.
ApplicationContext automatically detects any beans that implement BeanPostProcessor in the configuration metadata and registers them as post‑processors.
Note: When declaring a BeanPostProcessor with a @Bean factory method, the method’s return type should be the implementation class itself or at least the org.springframework.beans.factory.config.BeanPostProcessor interface, so that ApplicationContext can detect it early.
Programmatic registration of BeanPostProcessor instances Although automatic detection is preferred, you can register BeanPostProcessor instances programmatically via addBeanPostProcessor on a configurable BeanFactory. This is useful for conditional registration or copying processors across contexts, but such instances do not follow the ordered interface; their execution order follows the registration order.
BeanPostProcessor instances and AOP auto‑proxy Classes implementing BeanPostProcessor are treated specially by the container. They are instantiated early, sorted, and applied to all other beans. Because AOP auto‑proxying itself is implemented as a BeanPostProcessor, BeanPostProcessor instances are not eligible for auto‑proxying. If a bean does not meet the conditions for processing by BeanPostProcessor (e.g., not eligible for auto‑proxy), an informational log message is emitted. Using @Autowired or @Resource may cause the container to access unexpected beans when matching dependencies, affecting proxy eligibility.
Example:
@Component
public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {
// Return the bean instance as‑is
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean; // you could return any object reference here
}
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("Bean '" + beanName + "' created : " + bean.toString());
return bean;
}
}AutowiredAnnotationBeanPostProcessor
Combining callback interfaces or annotations with custom BeanPostProcessor implementations is a common way to extend the Spring IoC container. Spring’s AutowiredAnnotationBeanPostProcessor is an example of a BeanPostProcessor.
Customizing Configuration Metadata with BeanFactoryPostProcessor
The next extension point is
org.springframework.beans.factory.config.BeanFactoryPostProcessor. Its semantics are similar to BeanPostProcessor but it operates on bean configuration metadata before any other beans (except other BeanFactoryPostProcessor instances) are instantiated.
You can configure multiple BeanFactoryPostProcessor instances and control their order via the order property, which is effective only when the processor implements the ordered interface.
If you want to change actual bean instances (i.e., objects created from configuration metadata), you should use BeanPostProcessor instead. Using BeanFactoryPostProcessor to obtain bean instances can cause premature instantiation and violate the container lifecycle. The scope of a BeanFactoryPostProcessor is per container; it only affects bean definitions within the container where it is defined.
BeanFactoryPostProcessor beans are automatically run when declared in an ApplicationContext, allowing you to modify configuration metadata before bean creation. Spring provides many predefined processors such as PropertyOverrideConfigurer and PropertySourcesPlaceholderConfigurer, and you can also register custom ones.
Externalizing Properties with PropertySourcesPlaceholderConfigurer
By using standard Java properties files, you can externalize property values in bean definitions with PropertySourcesPlaceholderConfigurer, enabling environment‑specific configuration without modifying XML definitions.
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"/>
<bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>At runtime, PropertySourcesPlaceholderConfigurer replaces placeholders of the form ${propertyName} with values from a standard Java .properties file.
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=rootCustom BeanFactoryPostProcessor
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// Retrieve the BeanDefinition for "person" and modify its "name" property
BeanDefinition beanDefinition = beanFactory.getBeanDefinition("person");
beanDefinition.getPropertyValues().addPropertyValue("name", "CNM");
System.out.println("BeanFactoryPostProcessor");
}
}Custom Instantiation Logic Using FactoryBean
You can implement the org.springframework.beans.factory.FactoryBean interface for objects that themselves act as factories.
FactoryBean provides three methods:
T getObject(): Returns an instance created by the factory. The instance may be shared depending on whether the factory is singleton or prototype.
boolean isSingleton(): Returns true if the FactoryBean produces a singleton, otherwise false . The default implementation returns true .
Class getObjectType(): Returns the type of object returned by getObject() , or null if unknown.
Many Spring components use the FactoryBean pattern; over 50 implementations are provided by the framework.
When you need the FactoryBean instance itself rather than the product, prepend an ampersand ( &) to the bean name in ApplicationContext.getBean(). For example, getBean("myBean") returns the product, while getBean("&myBean") returns the FactoryBean instance.
End of article.
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.
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.
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.
