
1. Overview
In this article, we will discuss the BeanFactoryPostProcessor with an example.
2. BeanFactoryPostProcessor
A bean factory post-processor is a java class that implements the org.springframework.beans.factory.config.BeanFactoryPostProcessor
interface. Based on the Spring container type, you must execute manually:
BeanFactory
- ManualApplicationContext
- Automatic.
To know the differences between BeanFactory and ApplicationContext, see this article.
The Bean factory post-processors enable you to apply changes to the BeanFactory
or ApplicationContext
after its construction.
Spring includes several pre-existing bean factory post-processors, such as:
PropertyResourceConfigurer
PropertyPlaceHolderConfigurer
BeanNameAutoProxyCreator
- useful for wrapping other beans transactionally or with any other kind of proxy.
You can also implement the BeanFactoryPostProcessor
interface and create custom bean factory post-processors.
As mentioned earlier, you must manually apply the BeanFactoryPostProcessor
in case of BeanFactory
. For example, the following code applies the PropertyPlaceholderConfigurer
manually.
BeanDefinitionRegistry beanDefinitionRegistry = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanDefinitionRegistry); reader.loadBeanDefinitions(new ClassPathResource("config.xml")); PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer(); cfg.setLocation(new ClassPathResource("jdbc.properties")); cfg.postProcessBeanFactory(beanDefinitionRegistry);
However, an ApplicationContext
will detect any beans which are deployed into it which implement the BeanFactoryPostProcessor
interface, and automatically use them as bean factory post-processors.
Nothing else needs to be done other than deploying these post-processor in a similar fashion to any other bean.
Since this manual step is not convenient, and ApplictionContexts
are functionally supersets of BeanFactories
, it is generally recommended that ApplicationContext
variants are used when bean factory post-processors are needed.
Let’s discuss the BeanFactoryPostProcessor example.
3. BeanFactoryPostProcessor example
3.1. PropertyPlaceholderConfigurer example
The PropertyPlaceholderConfigurer
, implemented as a bean factory post-processor, is used to externalize some property values from a BeanFactory definition, into another separate file in Java Properties format.
This is useful to allow the person deploying an application to customize some key properties (for example database URLs, usernames and passwords), without the complexity or risk of modifying the main XML definition file or files for the BeanFactory
.
Assume a Bean definition where a DataSource
with placeholder values is defined:
For example, we defined a data source, and configured some properties from an external Properties file.
At runtime, we will apply a PropertyPlaceholderConfigurer
to the BeanFactory
which will replace some properties of the datasource:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <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>
The actual values come from another file in Properties format:
jdbc.driverClassName=org.hsqldb.jdbcDriver jdbc.url=jdbc:hsqldb:hsql://production:9002 jdbc.username=sa jdbc.password=root
To use this with a BeanFactory
, you need to execute the bean factory post-processor manually:
BeanDefinitionRegistry beanDefinitionRegistry = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanDefinitionRegistry); reader.loadBeanDefinitions(new ClassPathResource("config.xml")); PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer(); cfg.setLocation(new ClassPathResource("jdbc.properties")); cfg.postProcessBeanFactory(beanDefinitionRegistry);
Note thatApplicationContexts
can automatically recognize and apply beans deployed in them which implementBeanFactoryPostProcessor
.
The PropertyPlaceHolderConfigurer
looks not only for properties in the Properties file you specify but also checks against the Java System properties if it cannot find a property you are trying to use.
We can customize this behavior by setting the systemPropertiesMode
property of the configure.
SYSTEM_PROPERTIES_MODE_OVERRID
- always overrideSYSTEM_PROPERTIES_MODE_NEVER
- never overrideSYSTEM_PROPERTIES_MODE_FALLBACK
- override only if the property not found in the specified properties file
cfg.setSystemPropertiesMode(PropertyPlaceHolderConfigurer.SYSTEM_PROPERTIES_MODE_NEVER);
3.2. PropertyOverrideConfigurer BeanFactoryPostProcessors
The PropertyOverrideConfigurer
, another bean factory post-processor, is like the PropertyPlaceholderConfigurer
, but in contrast to the latter, the original bean definitions can have default values or no values at all for bean properties.
If an overriding Properties file does not have an entry for a certain bean property, it uses the default context definition.
Note that the bean factory definition is not aware of being overridden, so it is not immediately obvious when looking at the XML definition file that the override configurer
is being used.
In case that there are multiple PropertyOverrideConfigurers that define different values for the same bean property, the last one will win (because of the overriding mechanism).
You must specify the bean name followed by the property name in your Properties file configuration:
beanName.property=value
For example, the dataSource
is the bean name and driverClassName
and URL
are properties:
dataSource.driverClassName=com.mysql.jdbc.Driver dataSource.url=jdbc:mysql:mydb
3. Conclusion
To sum up, we have learned the BeanFactoryPostProcessor with a pre-existing processor example. You can refer to our GitHub repository for code samples.