3.13 BeanPostProcessors & BeanFactoryPostProcessors

Many processes in the IoC container were made to be extensible. A specific extensible process can be referred to as an extension point. One extension point, the BeanPostProcessor interface, allows the modifcation of a bean instance before and after the properties are set. Another extension point is the BeanFactoryPostProcessor interface which allows direct modification of bean definitions before a bean is instantiated. An ApplicationContext will automatically register and process a bean that implements either of these interfaces, but a BeanFactory would have to have a BeanPostProcessor or BeanFactoryPostProcessor registered with it programatically. The Ordered interface can be used to control the order in which these post processors are run relative to each other.

BeanPostProcessor

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. You could use this to process fields that were set, perform validation on a bean, or even look up values from a remote resource to set on the bean as defaults.

BeanPostProcessors 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. So, any BeanPostProcessor you create isn't eligible for AOP proxies. Since AOP proxies are applied this way, it's possible an AOP proxy may not yet have been applied to the instance so care should be taken if this will affect any post processing being done.

Below is a list of all classes that implement the BeanPostProcessor interface.

  • AbstractAdvisorAutoProxyCreator
  • AbstractAutoProxyCreator
  • AdvisorAdapterRegistrationManager
  • AnnotationAwareAspectJAutoProxyCreator
  • AspectJAwareAdvisorAutoProxyCreator
  • AutowiredAnnotationBeanPostProcessor
  • BeanNameAutoProxyCreator
  • CommonAnnotationBeanPostProcessor
  • DefaultAdvisorAutoProxyCreator
  • InfrastructureAdvisorAutoProxyCreator
  • InitDestroyAnnotationBeanPostProcessor
  • InstantiationAwareBeanPostProcessorAdapter
  • LoadTimeWeaverAwareProcessor
  • PersistenceAnnotationBeanPostProcessor
  • PersistenceExceptionTranslationPostProcessor
  • PortletContextAwareProcessor
  • RequiredAnnotationBeanPostProcessor
  • ScriptFactoryPostProcessor
  • ServletContextAwareProcessor
  • SimplePortletPostProcessor
  • SimpleServletPostProcessor

Example 3.44. DoublerBeanPostProcessor

This example shows a very simple BeanPostProcessor that takes a value from an int field and doubles it if the getter or setter is marked with the @Doubler annotation.

chapter03-post-processor/src/main/java/org/springbyexample/springindepth/chapter03/postProcessor/DoublerBean.java
                    
/**
 * Doubler bean.
 */
public class DoublerBean {
    
    protected int value = 0;

    /**
     * Gets value.
     */
    @Doubler
    public int getValue() {
        return value;
    }

    /**
     * Sets value.
     */
    public void setValue(int value) {
        this.value = value;
    }
    
}
                    
                

The DoublerBeanPostProcessor implements the Ordered interface configured with the lowest order of precedence. The postProcessBeforeInitialization method loops through all methods using Springs ReflectionUtils and checks for the presence of the @Doubler annotation on the method. If it finds a method with the annotation, it assumes it is an int with a getter and setter. The post processor then doubles the value and sets it.

chapter03-post-processor/src/main/java/org/springbyexample/springindepth/chapter03/postProcessor/DoublerBeanPostProcessor.java
                    
package org.springbyexample.springindepth.chapter03.postProcessor;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.util.ReflectionUtils;

/**
 * Doubles values for the <code>Doubler</code> interface 
 * and is an implementation of <code>BeanPostProcessor</code>.
 */
public class DoublerBeanPostProcessor implements BeanPostProcessor, Ordered {

    final Logger logger = LoggerFactory.getLogger(DoublerBeanPostProcessor.class);

    /**
     * Implementation of <code>Ordered</code> interface.
     */
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }
    
    /**
     * Process before bean initialization.
     */
    public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
        logger.debug("Doubler before initialization of '" + beanName + "' bean.");

        Class clazz = bean.getClass();
        
        ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() {
            public void doWith(Method method) {
                if (method.isAnnotationPresent(Doubler.class)) {
                    try {
                        PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);

                        int originalValue = (Integer) pd.getReadMethod().invoke(bean, null);
                        int doubledValue = originalValue * 2;

                        // set doubled value
                        pd.getWriteMethod().invoke(bean, new Object[] { doubledValue });

                        logger.debug("Doubled value." + 
                                     "  originalValue=" + originalValue + 
                                     "  doubledValue=" + doubledValue);
                    } catch (Throwable e) {
                        logger.error(e.getMessage(), e);
                    }
                }
            }
        });

        return bean;
    }

    /**
     * Process after bean initialization.
     */
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        logger.debug("Doubler after initialization of '" + beanName + "' bean.");

        return bean;
    }

}
                    
                

The DoublerBeanPostProcessor is automatically registered when the configuration is loaded by the ApplicationContext. The doubler bean's value of 2 will be doubled to 4 after it's instantiated and the post processor is run on it.

Excerpt from chapter03-post-processor/src/main/resources/applicationContext.xml
                    
<bean class="org.springbyexample.springindepth.chapter03.postProcessor.DoublerBeanPostProcessor" />

...

<bean id="doubler"
    class="org.springbyexample.springindepth.chapter03.postProcessor.DoublerBean">
    <property name="value" value="2" />
</bean>
                    
                

Example 3.45. EvaluatorBeanPostProcessor

Spring has support for dynamic languages. It supports JRuby, Groovy, and BeanShell. The lang namespace was added to support references to scripts and inline scripts for each language. The following example uses an inline Jruby script to evaluate an expression.

The EvaluatorBeanPostProcessor evaluates an expression and then set the value on a bean. The expression is expected to generate an int. In the RubyEvaluator class defined in the script, the include keyword will make the class implement the Java interface referenced.

Excerpt from chapter03-post-processor/src/main/resources/applicationContext.xml
                    
<bean class="org.springbyexample.springindepth.chapter03.postProcessor.EvaluatorBeanPostProcessor">
    <property name="evaluator" ref="jrubyEvaluator" />
</bean>

...

<bean id="evaluator"
    class="org.springbyexample.springindepth.chapter03.postProcessor.EvalBean">
    <property name="expression" value="2 * 4" />
</bean>

<lang:jruby id="jrubyEvaluator"
            script-interfaces="org.springbyexample.springindepth.chapter03.postProcessor.Evaluator">
    <lang:inline-script>
        <![CDATA[ 
            require 'java'

            class RubyEvaluator
                include org.springbyexample.springindepth.chapter03.postProcessor.Evaluator
                
                def evaluate(expression) 
                    eval(expression) 
                end

            end
        ]]<
    </lang:inline-script>
</lang:jruby>
                    
                

BeanFactoryPostProcessor

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. They let you put placeholders as property values that are then replaced with values from property file. This is very useful for configuring a database connection pool, mail server, etc.

  • AspectJWeavingEnabler
  • CustomAutowireConfigurer
  • CustomEditorConfigurer
  • CustomScopeConfigurer
  • PreferencesPlaceholderConfigurer
  • PropertyOverrideConfigurer
  • PropertyPlaceholderConfigurer
  • PropertyResourceConfigurer
  • ServletContextPropertyPlaceholderConfigurer

Example 3.46. PropertyPlaceholderConfigurer

Excerpt from chapter03-post-processor/src/main/resources/applicationContext.xml
                    
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations" value="person.properties" />
</bean>
                    
                

Example 3.47. Context Namespace Property Placeholder

Excerpt from chapter03-post-processor/src/main/resources/context-applicationContext.xml
                    
<context:property-placeholder location="person.properties"/>

<bean id="person"
      class="org.springbyexample.springindepth.bean.Person">
    <property name="firstName" value="${firstName}" />
    <property name="lastName" value="${lastName}" />
</bean>