Core Spring : BeanPostProcessor & BeanFactoryPostProcessor

A BeanPostProcessor gives you a chance to process an instance of a bean created by the IoC container after it’s instantiation and then again after the initialization lifecycle event has occurred on the instance. BeanPostProcessor and any beans they depend on are instantiated before any other beans in the container. After they are instantiated and ordered, they are used to process all the other beans as they are instantiated by the IoC container. Spring’s different AOP proxies for caching, transactions, etc. are all applied by BeanPostProcessors.

We just have to implement BeanPostProcessor interface in a class and map it to our spring.xml. Employee and address bean:


package mynotes.corespring.beanpostprocessor;

public class Employee{

private int id;
private String name;
private Address homeAddress;

public Employee(int id, String name) {
this.id = id;
this.name = name;
}

@Override
public String toString() {
return ("Id=>" + this.id + " Name=>" + this.name+ "\nhomeAddress=>"
+ this.homeAddress );
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Address getHomeAddress() {
return homeAddress;
}

public void setHomeAddress(Address homeAddress) {
this.homeAddress = homeAddress;
}

}


package mynotes.corespring.beanpostprocessor;

public class Address {

private String street;
private String city;
private int pincode;

@Override
public String toString() {
return ("street::" + this.street + " city::" + this.city + " pincode::"
+ this.pincode);
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public int getPincode() {
return pincode;
}
public void setPincode(int pincode) {
this.pincode = pincode;
}

}

Our DisplayNameBeanPostProcessor.java


package mynotes.corespring.beanpostprocessor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class DisplayNameBeanPostProcessor implements BeanPostProcessor {

public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("In After bean Initialization method. Bean name is "
+ beanName);
return bean;
}

public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
System.out
.println("In Before bean Initialization method. Bean name is "
+ beanName);
return bean;
}
}

Notice that there are two methods that we need to implements which takes the object bean and the bean name as input. Also we have to return the bean , else the flow in spring will be disrupted. Mapping all these beans in our spring.xml:


<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">

<bean id="employee" class="mynotes.corespring.beanpostprocessor.Employee">
<constructor-arg value="John" type="String"></constructor-arg>
<constructor-arg value="20" type="int"></constructor-arg>
<property name="homeAddress" ref="homeAddress"></property>
</bean>
<bean id="homeAddress" class="mynotes.corespring.beanpostprocessor.Address">
<property name="street" value="Street1"></property>
<property name="city" value="City1"></property>
<property name="pincode" value="123456"></property>
</bean>
<bean class="mynotes.corespring.beanpostprocessor.DisplayNameBeanPostProcessor"/>

</beans>

Our App.java for test


package mynotes.corespring.beanpostprocessor;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {

public static void main(String[] args) {
AbstractApplicationContext context=new ClassPathXmlApplicationContext("/mynotes/corespring/beanpostprocessor/spring.xml");
context.registerShutdownHook();
Employee employee=(Employee) context.getBean("employee");
System.out.println(employee);

}

}

Output:


In Before bean Initialization method. Bean name is homeAddress
In After bean Initialization method. Bean name is homeAddress
In Before bean Initialization method. Bean name is employee
In After bean Initialization method. Bean name is employee
Id=>20 Name=>John
homeAddress=>street::Street1 city::City1 pincode::123456

Difference from the init/destroy method in spring – One of the obvious difference is that beanpostprocessor is in a seperate class while the init/destroy method resides in the bean. Also, there is no destroy method in beanPostProcessor.

Spring uses BeanPostProcessors everywhere to enable features such as security, transaction processing, remoting, JMX monitoring, and a number of other features.

A BeanFactoryPostProcessor lets you modify the actual bean definition instead of the instance as it’s created. The PropertyResourceConfigurer and PropertyPlaceholderConfigurer are two very useful BeanFactoryPostProcessors.  Creatinng out custom Factory post processor:


package mynotes.corespring.beanpostprocessor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor{

public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0)
throws BeansException {
System.out.println("In postProcessBeanFactory");

}

}

Map this in the spring.xml file and run App.java. Output:


In postProcessBeanFactory
In Before bean Initialization method. Bean name is homeAddress
In After bean Initialization method. Bean name is homeAddress
In Before bean Initialization method. Bean name is employee
In After bean Initialization method. Bean name is employee
Id=>20 Name=>John
homeAddress=>street::Street1 city::City1 pincode::123456

Notice that the postProcessBeanFactory method ran before any bean is made.   It is executed manually (in the case of the BeanFactory) or automatically (in the case of the  ApplicationContext) to apply changes of some sort to an entire BeanFactory, after it has been constructed.
We can use PropertyPlaceholderConfigurer class to externalize some details into a properties file, and access from bean configuration file via a special format – ${variable}.

Lets create a property file – test.properties


homeaddress.street=street1
homeaddress.city=city1
homeaddress.pincode=123123

Changing our spring.xml, other things to remain same:


<bean id="homeAddress" class="mynotes.corespring.beanpostprocessor.Address">
<property name="street" value="${homeaddress.street}"></property>
<property name="city" value="${homeaddress.city}"></property>
<property name="pincode" value="${homeaddress.pincode}"></property>
</bean>

<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations"
value="classpath:mynotes/corespring/beanpostprocessor/test.properties" />
</bean>

Running App.java will produce the same result. Often there are different propeties for different environments like db-test.properties, db-prod.properties etc. , you can use


<property name="locations"
value="classpath:db-${my.env}.properties" />

my.env will be taken from  JVM System property.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: