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.
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.
AbstractAdvisorAutoProxyCreatorAbstractAutoProxyCreatorAdvisorAdapterRegistrationManagerAnnotationAwareAspectJAutoProxyCreatorAspectJAwareAdvisorAutoProxyCreatorAutowiredAnnotationBeanPostProcessorBeanNameAutoProxyCreatorCommonAnnotationBeanPostProcessorDefaultAdvisorAutoProxyCreatorInfrastructureAdvisorAutoProxyCreatorInitDestroyAnnotationBeanPostProcessorInstantiationAwareBeanPostProcessorAdapterLoadTimeWeaverAwareProcessorPersistenceAnnotationBeanPostProcessorPersistenceExceptionTranslationPostProcessorPortletContextAwareProcessorRequiredAnnotationBeanPostProcessorScriptFactoryPostProcessorServletContextAwareProcessorSimplePortletPostProcessorSimpleServletPostProcessor
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.
/**
* 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.
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.
<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.
<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>
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.
AspectJWeavingEnablerCustomAutowireConfigurerCustomEditorConfigurerCustomScopeConfigurerPreferencesPlaceholderConfigurerPropertyOverrideConfigurerPropertyPlaceholderConfigurerPropertyResourceConfigurerServletContextPropertyPlaceholderConfigurer
Example 3.46. PropertyPlaceholderConfigurer
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="person.properties" />
</bean>
Example 3.47. Context Namespace Property Placeholder
<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>