BeanFactoryPostProcessor interface in Spring resides in org.springframework.beans.factory.config package. BeanFactoryPostProcessor implementation is used to read the configuration metadata and potentially change it before beans are instantiated by IOC container.
You can configure multiple BeanFactoryPostProcessors, you can also control the order in which these BeanFactoryPostProcessors execute by setting the order property. You can set the order property only if the BeanFactoryPostProcessor implements the Ordered interface.
BeanFactoryPostProcessor interface in Spring
BeanFactoryPostProcessor interface is a functional interface meaning it has a single abstract method, that method is postProcessBeanFactory() which you need to implement in order to custom modify the bean definitions. Note that when this method is called at that time all bean definitions will have been loaded, but no beans will have been instantiated yet. This allows for overriding or adding properties even to eager-initializing beans.
@FunctionalInterface public interface BeanFactoryPostProcessor { void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException; }
Usage of BeanFactoryPostProcessor in Spring
The implementations of BeanFactoryPostProcessor interface are used by Spring framework itself. When you read from property files in Spring and configure the <context:property-placeholder> element that registers PropertySourcesPlaceholderConfigurer which implements BeanFactoryPostProcessor interface and set the properties there in the bean.
Spring BeanFactoryPostProcessor example
Here let’s have a simple example of BeanFactoryPostProcessor in Spring.
The scenario is that you have set the properties in a property file for DB config but for a particular run you want to use the separate schema which is set up in such a way that all the otehr properties remain same except the url. Which means you want to override the url property of the DataSource and modify it so that you can connect to the new Schema.
Though a better option would be to create separate Spring profiles and switch among those profiles but you can access bean definition and modify the value of the property using the BeanFactoryPostProcessor.
db.properties file
db.driverClassName=com.mysql.jdbc.Driver db.url=jdbc:mysql://localhost:3306/netjs db.username=root db.password=admin pool.initialSize=5
XML configuration for the datasource
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"> <property name="driverClassName" value = "${db.driverClassName}" /> <property name="url" value = "${db.url}" /> <property name="username" value = "${db.username}" /> <property name="password" value = "${db.password}" /> <property name="initialSize" value = "${pool.initialSize}" / </bean>
BeanFactoryPostProcessor implementation class
import org.springframework.beans.BeansException; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.PropertyValue; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.core.Ordered; public class TestDBPostProcessor implements BeanFactoryPostProcessor, Ordered { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println("In postProcessBeanFactory"); // Getting the dataSource bean BeanDefinition bd = beanFactory.getBeanDefinition("dataSource"); if(bd.hasPropertyValues()){ MutablePropertyValues pvs = bd.getPropertyValues(); PropertyValue[] pvArray = pvs.getPropertyValues(); for (PropertyValue pv : pvArray) { System.out.println("pv -- " + pv.getName()); // changing value for url property if(pv.getName().equals("url")){ pvs.add(pv.getName(), "jdbc:mysql://localhost:3306/TestSchema"); } } } } @Override public int getOrder() { // TODO Auto-generated method stub return 0; } }
As you can see in the method postProcessBeanFactory() you can get the dataSource bean and modify the bean definition.
To register the BeanFactoryPostProcessor add the following line in your configuration.
<bean class="org.netjs.config.TestDBPostProcessor" />
Here is the method where I want to use the new schema.
public List<Employee> findAllEmployees() { System.out.println("URL " + ((BasicDataSource)jdbcTemplate.getDataSource()).getUrl()); return this.jdbcTemplate.query(SELECT_ALL_QUERY, (ResultSet rs) -> { List<Employee> list = new ArrayList<Employee>(); while(rs.next()){ Employee emp = new Employee(); emp.setEmpId(rs.getInt("id")); emp.setEmpName(rs.getString("name")); emp.setAge(rs.getInt("age")); list.add(emp); } return list; }); }To run this example following code can be used.
public class App { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext ("appcontext.xml"); EmployeeDAO dao = (EmployeeDAO)context.getBean("employeeDAOImpl"); List<Employee> empList = dao.findAllEmployees(); for(Employee emp : empList){ System.out.println("Name - "+ emp.getEmpName() + " Age - " + emp.getAge()); } context.close(); } }
Output
Relevant lines from the console. In postProcessBeanFactory pv -- driverClassName pv -- url pv -- username pv -- password pv – initialSize URL jdbc:mysql://localhost:3306/TestSchema
That's all for this topic BeanFactoryPostProcessor in Spring Framework. If you have any doubt or any suggestions to make please drop a comment. Thanks!
>>>Return to Spring Tutorial Page
Related Topics
You may also like-
i this could be a silly question but I'm curios to know why the word post in BeanFactoryPostProcessor interface in Spring. After what are the BeanFactoryPostProcessor going to be used ? What happened before. I would like to know like why the name.
ReplyDeleteBecause it works after all bean definitions are loaded, but no beans will have been instantiated yet.
Delete