Friday, September 3, 2021

BeanFactoryPostProcessor in Spring Framework

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

  1. BeanPostProcessor in Spring Framework
  2. ApplicationContextAware And BeanNameAware Interfaces in Spring Framework
  3. BeanFactoryAware Interface in Spring Framework
  4. Injecting Inner Bean in Spring
  5. Circular Dependency in Spring Framework

You may also like-

  1. Spring JdbcTemplate Insert, Update And Delete Example
  2. Spring JdbcTemplate With ResultSetExtractor Example
  3. Spring Expression Language (SpEL) With Examples
  4. Spring depends-on Attribute
  5. How HashMap Internally Works in Java
  6. Covariant Return Type in Java
  7. Spliterator in Java
  8. Replica Placement Policy in Hadoop Framework

2 comments:

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

    ReplyDelete
    Replies
    1. Because it works after all bean definitions are loaded, but no beans will have been instantiated yet.

      Delete