- Back to Home »
- Spring »
- The BeanPostProcessor
The BeanPostProcessor
Sometimes, youmay find yourself in a position where you need to performsome additional processing immediately before and after Spring instantiates the bean. The processing can be as simple as modifying the bean or as complex as returning a completely different object! The BeanPostProcessor interface has two methods: postProcessBeforeInitialization, which is called before Spring calls any bean initialization hooks (such as InitializingBean.afterPropertiesSet or the init-method), and postProcessAfterInitialization, which Spring calls after the initialization hooks succeed.
Annotated Bean Example
public class SimpleBean {
@PostConstruct
public void initialize() {
System.out.println("Initializing bean " + getClass());
}
@PreDestroy
public void cleanUp() {
System.out.println("Cleaning up bean " + getClass());
}
}
We would like Spring to use these annotations automatically, so we do not want to specify the init-method or destroy-method attributes in the <bean> definition in the BeanFactory configuration.
However, we can use InitDestroyAnnotationBeanPostProcessor, an implementation of BeanPostProcessor that invokes all methods on the target bean with the configured initialization annotation. Because it also implements the DestructionAwareBeanPostProcessor, it invokes all methods on the target bean annotated
with the destruction annotation
BeanFactory Configuration File
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="simpleBean" class="com.apress.prospring2.ch04.bpp.SimpleBean"/>
<bean id="bpp" class="org.springframework.beans.factory.annotation.➥
InitDestroyAnnotationBeanPostProcessor">
<property name="initAnnotationType" value="javax.annotation.PostConstruct"/>
<property name="destroyAnnotationType" value="javax.annotation.PreDestroy"/>
</bean>
</beans>
You can see that we have declared the simpleBean bean without init-method or destroy-method. We have also declared the InitDestroyAnnotationBeanPostProcessor and set its initAnnotationType and destroyAnnotationType to the JSR 250 annotations. This code may look complicated, but it means that we can specify any type of annotation;
Sample Application for the InitDestroyAnnotationBeanPostProcessor
public class SimpleBeanDemo {
public static void main(String[] args) {
ConfigurableListableBeanFactory beanFactory = new XmlBeanFactory(
new ClassPathResource("/META-INF/spring/bpp-context.xml")
);
BeanPostProcessor bpp = (BeanPostProcessor)beanFactory.getBean("bpp");
beanFactory.addBeanPostProcessor(bpp);
SimpleBean sb = (SimpleBean)beanFactory.getBean("simpleBean");
System.out.println(sb);
beanFactory.destroySingletons();
}
}
INFO [main] XmlBeanDefinitionReader.loadBeanDefinitions(308) | ➥
Loading XML bean definitions from class path resource ➥
[META-INF/spring/bpp-context.xml]
Initializing bean class com.apress.prospring2.ch04.bpp.SimpleBean
com.apress.prospring2.ch04.bpp.SimpleBean@c16c2c0
INFO [main] DefaultSingletonBeanRegistry.destroySingletons(340) | ➥
Destroying singletons in org.springframework.beans.factory.xml.➥
XmlBeanFactory@2a4bd173: defining beans [simpleBean,bpp]; root of factory hierarchy
Cleaning up bean class com.apress.prospring2.ch04.bpp.SimpleBean
Implementing a BeanPostProcessor
Let’s create a simple BeanPostProcessor that can timestamp our beans. The aim is to set the Date fields annotated with the @Timestamp annotation
@Timestamp Annotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Timestamp {
}
Modified SimpleBean with the @Timestamp Field
public class SimpleBean {
@Timestamp
Date creationDate;
@PostConstruct
public void initialize() {
System.out.println("Initializing bean " + getClass());
}
@PreDestroy
public void cleanUp() {
System.out.println("Cleaning up bean " + getClass());
}
@Override
public String toString() {
return "Bean was created at " + this.creationDate;
}
}
TimestampingBeanPostProcessor
public class TimestampingBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(final Object bean,
final String beanName) throws BeansException {
ReflectionUtils.doWithFields(bean.getClass(),
new ReflectionUtils.FieldCallback() {
public void doWith(Field field) throws IllegalArgumentException,
IllegalAccessException {
field.set(bean, new Date());
}
}, new ReflectionUtils.FieldFilter() {
public boolean matches(Field field) {
return field.getType() == Date.class &&
field.getAnnotation(Timestamp.class) != null;
}
});
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
}
The TimestampingBeanPostProcessor Bean
<bean id="bpp2" lass="com.apress.prospring2.ch04.bpp.TimestampingBeanPostProcessor"/>
The modification to the sample application is very simple too;
Modified Sample Application
public class SimpleBeanDemo {
public static void main(String[] args) {
ConfigurableListableBeanFactory beanFactory = new XmlBeanFactory(
new ClassPathResource("/META-INF/spring/bpp-context.xml")
);
BeanPostProcessor bpp = (BeanPostProcessor)beanFactory.getBean("bpp");
BeanPostProcessor bpp2 = (BeanPostProcessor)beanFactory.getBean("bpp2");
beanFactory.addBeanPostProcessor(bpp);
beanFactory.addBeanPostProcessor(bpp2);
SimpleBean sb = (SimpleBean)beanFactory.getBean("simpleBean");
System.out.println(sb);
beanFactory.destroySingletons();
}
}
INFO [main] XmlBeanDefinitionReader.loadBeanDefinitions(308) | ➥
Loading XML bean definitions from class path resource ➥
[META-INF/spring/bpp-context.xml]
Initializing bean class com.apress.prospring2.ch04.bpp.SimpleBean
Bean was created at Fri Mar 28 11:00:02 GMT 2008
INFO [main] DefaultSingletonBeanRegistry.destroySingletons(340) | ➥
Destroying singletons in org.springframework.beans.factory.xml.➥
XmlBeanFactory@f292738: defining beans [simpleBean,bpp,bpp2]; ➥
root of factory hierarchy
Cleaning up bean class com.apress.prospring2.ch04.bpp.SimpleBean
SimpleBean with the @Autowired, @PostConstruct, and @PreDestroy Annotations
public class SimpleBean {
@Timestamp
Date creationDate;
@Autowired
String dependency;
@PostConstruct
public void initialize() {
System.out.println("Initializing bean " + getClass());
}
@PreDestroy
public void cleanUp() {
System.out.println("Cleaning up bean " + getClass());
}
@Override
public String toString() {
return "Bean was created at " + this.creationDate + " with " +
this.dependency;
}
}
Using InstantiationAwareBeanPostProcessorAdapter
public class TypedDependencyBeanPostProcessor extends
InstantiationAwareBeanPostProcessorAdapter {
public Class predictBeanType(Class beanClass, String beanName) {
if (beanClass.equals(Dependency.class)) {
return String.class;
}
return beanClass;
}
public Object postProcessBeforeInitialization(final Object bean,
final String beanName)
throws BeansException {
if (bean.getClass().equals(Dependency.class)) {
return "Hello, world";
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
}
The BeanPostProcessor implementation is very simple: we turn all Dependency objects into String objects, but we also predict that we’ll do just that in the predictBeanType method. Running the sample application now shows that Spring can automatically wire our String property:
INFO [main] XmlBeanDefinitionReader.loadBeanDefinitions(308) | ➥
Loading XML bean definitions from class path resource ➥
[META-INF/spring/bpp2-context.xml]
Initializing bean class com.apress.prospring2.ch04.bpp.SimpleBean
Bean was created at Fri Mar 28 11:44:59 GMT 2008 with Hello, world
INFO [main] DefaultSingletonBeanRegistry.destroySingletons(340) | ➥
Destroying singletons in org.springframework.beans.factory.xml.➥
XmlBeanFactory@6e681db8: defining beans ...
Cleaning up bean class com.apress.prospring2.ch04.bpp.SimpleBean