Container extension points
Typically, an application developer does not need to subclass any
ApplicationContext implementation classes.
You can extend The Spring IoC container infinitely by plugging in
implementations of special integration interfaces. The next few sections
describe these integration interfaces.
Customizing beans using the
BeanPostProcessor Interface
The BeanPostProcessor interface defines
callback methods that you can implement to provide
your own (or override the container's default) instantiation logic,
dependency-resolution logic, and so forth. If you want to implement some
custom logic after the Spring container finishes instantiating,
configuring, and otherwise initializing a bean, you can plug in one or
more BeanPostProcessor
implementations.
You can configure multiple BeanPostProcessor
interfaces. You can control the order in which these
BeanPostProcessor interfaces execute by setting the
order property. You can set this property only if the
BeanPostProcessor implements the
Ordered interface; if you write your own
BeanPostProcessor you should consider
implementing the Ordered interface too. For
more details, consult the Javadoc for the
BeanPostProcessor and
Ordered interfaces.
BeanPostProcessors operate on bean (or object)
instances; that is to say, the Spring IoC container
instantiates a bean instance and then
BeanPostProcessor interfaces do their work.
BeanPostProcessor interfaces are scoped
per-container. This is only relevant if you are
using container hierarchies. If you define a
BeanPostProcessor in one container, it
will only do its work on the beans in that
container. Beans that are defined in one container are not
post-processed by a BeanPostProcessor in another
container, even if both containers are part of the same
hierarchy.
To change the actual bean definition (that is, the recipe that
defines the bean), you instead need to use a
BeanFactoryPostProcessor, described below
in .
The
org.springframework.beans.factory.config.BeanPostProcessor
interface consists of exactly two callback methods. When such a class is
registered as a post-processor with the container, for each bean instance
that is created by the container, the post-processor gets a callback from
the container both before container initialization
methods (such as afterPropertiesSet and any declared
init method) are called, and also afterwards. The post-processor can take
any action with the bean instance, including ignoring the callback
completely. A bean post-processor typically checks for callback
interfaces, or may wrap a bean with a proxy. Some Spring AOP
infrastructure classes are implemented as bean post-processors and they do
this proxy-wrapping logic.
An ApplicationContext
automatically detects any beans that are defined in
the configuration metadata it receives that implement the
BeanPostProcessor interface. The
ApplicationContext registers these beans as
post-processors, to be then called appropriately by the container upon
bean creation. You can then deploy the post-processors as you would any
bean.
BeanPostProcessors and AOP
auto-proxying
Classes that implement the
BeanPostProcessor interface are
special, and so they are treated differently by the
container. All BeanPostProcessors
and their directly referenced beans are
instantiated on startup, as part of the special startup phase of the
ApplicationContext. Next, all those
BeanPostProcessors are registered in a
sorted fashion - and applied to all further beans. Because AOP
auto-proxying is implemented as a
BeanPostProcessor itself, no
BeanPostProcessors or directly referenced
beans are eligible for auto-proxying, and thus do not have aspects woven
into them.
For any such bean, you should see an info log message:
Bean foo is not eligible for getting processed by all
BeanPostProcessor interfaces (for example: not eligible for
auto-proxying)
.
The following examples show how to write, register, and use
BeanPostProcessors in the context of an
ApplicationContext.
Example: Hello World,
BeanPostProcessor-style
This first example illustrates basic usage. The example shows a
custom BeanPostProcessor implementation
that invokes the toString() method of each bean
as it is created by the container and prints the resulting string to the
system console.
Find below the custom
BeanPostProcessor implementation class
definition:
package scripting;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.BeansException;
public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {
// simply return the instantiated bean as-is
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean; // we could potentially return any object reference here...
}
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("Bean '" + beanName + "' created : " + bean.toString());
return bean;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/lang
http://www.springframework.org/schema/lang/spring-lang-3.0.xsd">
<lang:groovy id="messenger"
script-source="classpath:org/springframework/scripting/groovy/Messenger.groovy">
<lang:property name="message" value="Fiona Apple Is Just So Dreamy."/>
</lang:groovy>
<!--
when the above bean (messenger) is instantiated, this custom
BeanPostProcessor implementation will output the fact to the system console
-->
<bean class="scripting.InstantiationTracingBeanPostProcessor"/>
</beans>
Notice how the
InstantiationTracingBeanPostProcessor is simply
defined. It does not even have a name, and because it is a bean it can
be dependency-injected just like any other bean. (The preceding
configuration also defines a bean that is backed by a Groovy script. The
Spring 2.0 dynamic language support is detailed in the chapter entitled
.)
The following small driver script executes the preceding code and
configuration:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scripting.Messenger;
public final class Boot {
public static void main(final String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("scripting/beans.xml");
Messenger messenger = (Messenger) ctx.getBean("messenger");
System.out.println(messenger);
}
}
The output of the preceding execution resembles the
following:
Bean 'messenger' created : org.springframework.scripting.groovy.GroovyMessenger@272961
org.springframework.scripting.groovy.GroovyMessenger@272961
Example: The
RequiredAnnotationBeanPostProcessor
Using callback interfaces or annotations in conjunction with a
custom BeanPostProcessor implementation
is a common means of extending the Spring IoC container. An example is
Spring's RequiredAnnotationBeanPostProcessor -- a
BeanPostProcessor implementation that
ships with the Spring distribution which ensures that JavaBean
properties on beans that are marked with an (arbitrary) annotation are
actually (configured to be) dependency-injected with a value.
Customizing configuration metadata with
BeanFactoryPostProcessor interface
The next extension point that we will look at is the
org.springframework.beans.factory.config.BeanFactoryPostProcessor.
The semantics of this interface are similar to the
BeanPostProcessor, with one major
difference: BeanFactoryPostProcessors operate on the
bean configuration metadata; that is, the Spring IoC
container allows BeanFactoryPostProcessors to read the
configuration metadata and potentially change it
before the container instantiates any beans other
than BeanFactoryPostProcessors.
You can configure multiple
BeanFactoryPostProcessors. You can control the order in
which these BeanFactoryPostProcessors execute by
setting the order property. However, you can only set
this property if the
BeanFactoryPostProcessor implements the
Ordered interface. If you write your own
BeanFactoryPostProcessor, you should
consider implementing the Ordered interface
too; consult the Javadoc for the
BeanFactoryPostProcessor and
Ordered interfaces for more details.
If you want to change the actual bean instances
(the objects that are created from the configuration metadata), then you
instead need to use a BeanPostProcessor
(described above in . While
it is technically possible to work with bean instances within a
BeanFactoryPostProcessor (e.g. using
BeanFactory.getBean()), doing so causes
premature bean instantiation, violating the usual containter lifecycle.
This may cause negative side effects such as bypassing bean post
processing.
Also, BeanFactoryPostProcessors are scoped
per-container. This is only relevant if you are
using container hierarchies. If you define a
BeanFactoryPostProcessor in one
container, it will only do its stuff on the bean
definitions in that container. Bean definitions in another container
will not be post-processed by
BeanFactoryPostProcessors in another container, even
if both containers are part of the same hierarchy.
A bean factory post-processor is executed automatically when it is
declared inside of an ApplicationContext,
in order to apply changes to the configuration metadata that defines a
container. Spring includes a number of pre-existing bean factory
post-processors, such as PropertyOverrideConfigurer
and PropertyPlaceholderConfigurer. A custom
BeanFactoryPostProcessor can also be used,
for example, to register custom property editors.
An ApplicationContext detects any beans
that are deployed into it and that implement the
BeanFactoryPostProcessor interface. It
automatically uses these beans as bean factory post-processors, at the
appropriate time. You can then deploy these post-processor beans as you
would any other bean.
As with BeanPostProcessors, you typically do not
want BeanFactoryPostProcessors marked as
lazy-initialized. If they are marked as such, the Spring container never
instantiates them, and thus they cannot apply their custom logic. If you
use the default-lazy-init attribute on the
declaration of your <beans/> element, be sure
to mark your various
BeanFactoryPostProcessor bean definitions
with lazy-init="false".
Customizing instantiation logic with the
FactoryBean Interface
You implement the
org.springframework.beans.factory.FactoryBean
interface for objects that are themselves
factories.
The FactoryBean interface is a point of
pluggability into the Spring IoC container's instantiation logic. If you
have complex initialization code that is better expressed in Java as
opposed to a (potentially) verbose amount of XML, you can create your own
FactoryBean, write the complex
initialization inside that class, and then plug your custom
FactoryBean into the container.
The FactoryBean interface provides
three methods:
Object getObject(): returns an instance
of the object this factory creates. The instance can possibly be
shared, depending on whether this factory returns singletons or
prototypes.
boolean isSingleton(): returns
true if this
FactoryBean returns singletons,
false otherwise.
Class getObjectType(): returns the object
type returned by the getObject() method or
null if the type is not known in advance
The FactoryBean concept and interface
is used in a number of places within the Spring Framework; more than 50
implementations of the FactoryBean
interface ship with Spring itself.
When you need to ask a container for an actual
FactoryBean instance itself, not the bean
it produces, you preface the bean id with the ampersand symbol
& (without quotes) when calling the
getBean() method of the
ApplicationContext. So for a given
FactoryBean with an id of
myBean, invoking getBean("myBean")
on the container returns the product of the
FactoryBean, and invoking
getBean("&myBean") returns the
FactoryBean instance
itself.