The IoC component of the Spring Framework has been designed for
extension. There is typically no need for an application developer to
subclass any of the various BeanFactory
or
ApplicationContext
implementation classes.
The Spring IoC container can be infinitely extended by plugging in
implementations of special integration interfaces. The next few sections
are devoted to detailing all of these various integration
interfaces.
The first extension point that we will look at is the
BeanPostProcessor
interface. This
interface defines a number of callback methods
that you as an application developer can implement in order to provide
your own (or override the containers default) instantiation logic,
dependency-resolution logic, and so forth. If you want to do some custom
logic after the Spring container has finished instantiating, configuring
and otherwise initializing a bean, you can plug in one or more
BeanPostProcessor
implementations.
You can configure multiple BeanPostProcessors
if you wish. You can control the order in which these
BeanPostProcessors
execute by setting the
'order'
property (you can only set this property if
the BeanPostProcessor
implements the
Ordered
interface; if you write your own
BeanPostProcessor
you should consider
implementing the Ordered
interface too);
consult the Javadoc for the
BeanPostProcessor
and
Ordered
interfaces for more
details.
![]() | Note |
---|---|
If you want to change the actual bean definition (that is the
recipe that defines the bean), then you rather need to use a
Also, |
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 (see below for how
this registration is effected), for each bean instance that is created
by the container, the post-processor will get a callback from the
container both before any container initialization
methods (such as afterPropertiesSet and any
declared init method) are called, and also afterwards. The
post-processor is free to do what it wishes with the bean instance,
including ignoring the callback completely. A bean post-processor will
typically check for callback interfaces, or do something such as wrap a
bean with a proxy; some of the Spring AOP infrastructure classes are
implemented as bean post-processors and they do this proxy-wrapping
logic.
It is important to know that a
BeanFactory
treats bean post-processors
slightly differently than an
ApplicationContext
. An
ApplicationContext
will
automatically detect any beans which are defined in
the configuration metadata which is supplied to it that implement the
BeanPostProcessor
interface, and register
them as post-processors, to be then called appropriately by the
container on bean creation. Nothing else needs to be done other than
deploying the post-processors in a similar fashion to any other bean. On
the other hand, when using a BeanFactory
implementation, bean post-processors explicitly have to be registered,
with code like this:
ConfigurableBeanFactory factory = new XmlBeanFactory(...); // now register any needed BeanPostProcessor instances MyBeanPostProcessor postProcessor = new MyBeanPostProcessor(); factory.addBeanPostProcessor(postProcessor); // now start using the factory
This explicit registration step is not convenient, and this is one
of the reasons why the various
ApplicationContext
implementations are
preferred above plain BeanFactory
implementations in the vast majority of Spring-backed applications,
especially when using BeanPostProcessors
.
![]() | BeanPostProcessors and AOP auto-proxying |
---|---|
Classes that implement the
For any such bean, you should see an info log message: “Bean 'foo' is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)”. |
Find below some examples of how to write, register, and use
BeanPostProcessors
in the context of an
ApplicationContext
.
This first example is hardly compelling, but serves to
illustrate basic usage. All we are going to do is code a custom
BeanPostProcessor
implementation that
simply invokes the toString()
method of each
bean as it is created by the container and prints the resulting string
to the system console. Yes, it is not hugely useful, but serves to get
the basic concepts across before we move into the second example which
is actually useful.
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 doesn't even have a name, and because it is a bean it can
be dependency injected just like any other bean. (The above
configuration also just so happens to define a bean that is backed by
a Groovy script. The Spring 2.0 dynamic language support is detailed
in the chapter entitled Chapter 28, Dynamic language support.)
Find below a small driver script to exercise the above 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 executing the above program will be (something like) this:
Bean 'messenger' created : [email protected] [email protected]
Using callback interfaces or annotations in conjunction with a
custom BeanPostProcessor
implementation
is a common means of extending the Spring IoC container. This next
example is a bit of a cop-out, in that you are directed to the section
entitled Section 29.3.1, “@Required” which
demonstrates the usage of a custom
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.
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 will allow BeanFactoryPostProcessors
to
read the configuration metadata and potentially change it
before the container has actually instantiated any
other beans.
You can configure multiple
BeanFactoryPostProcessors
if you wish. You can
control the order in which these
BeanFactoryPostProcessors
execute by setting the
'order'
property (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.
![]() | Note |
---|---|
If you want to change the actual bean
instances (the objects that are created from the
configuration metadata), then you rather need to use a
Also, |
A bean factory post-processor is executed manually (in the case of
a BeanFactory
) or automatically (in the
case of an ApplicationContext
) to apply
changes of some sort to the configuration metadata that defines a
container. Spring includes a number of pre-existing bean factory
post-processors, such as
PropertyOverrideConfigurer
and
PropertyPlaceholderConfigurer
, both described
below. A custom BeanFactoryPostProcessor
can also be used to register custom property editors, for
example.
In a BeanFactory
, the process of
applying a BeanFactoryPostProcessor
is
manual, and will be similar to this:
XmlBeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml")); // bring in some property values from a Properties file PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer(); cfg.setLocation(new FileSystemResource("jdbc.properties")); // now actually do the replacement cfg.postProcessBeanFactory(factory);
This explicit registration step is not convenient, and this is one
of the reasons why the various
ApplicationContext
implementations are
preferred above plain BeanFactory
implementations in the vast majority of Spring-backed applications,
especially when using
BeanFactoryPostProcessors
.
An ApplicationContext
will detect
any beans which are deployed into it which implement the
BeanFactoryPostProcessor
interface, and
automatically use them as bean factory post-processors, at the
appropriate time. Nothing else needs to be done other than deploying
these post-processor in a similar fashion to any other bean.
![]() | Note |
---|---|
Just as in the case of |
The PropertyPlaceholderConfigurer
is used to externalize property values from a
BeanFactory
definition, into another
separate file in the standard Java Properties
format. This is useful to allow the person deploying an application to
customize environment-specific properties (for example database URLs,
usernames and passwords), without the complexity or risk of modifying
the main XML definition file or files for the container.
Consider the following XML-based configuration metadata
fragment, where a DataSource
with
placeholder values is defined. We will configure some properties from
an external Properties
file, and at runtime, we
will apply a PropertyPlaceholderConfigurer
to
the metadata which will replace some properties of the
DataSource:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <value>classpath:com/foo/jdbc.properties</value> </property> </bean> <bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean>
The actual values come from another file in the standard Java
Properties
format:
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root
With the context
namespace introduced in
Spring 2.5, it is possible to configure property placeholders with a
dedicated configuration element. Multiple locations may be provided as
a comma-separated list for the location
attribute.
<context:property-placeholder location="classpath:com/foo/jdbc.properties"/>
The PropertyPlaceholderConfigurer
doesn't
only look for properties in the Properties
file
you specify, but also checks against the Java
System
properties if it cannot find a property
you are trying to use. This behavior can be customized by setting the
systemPropertiesMode
property of the configurer. It
has three values, one to tell the configurer to always override, one
to let it never override and one to let it
override only if the property cannot be found in the properties file
specified. Please consult the Javadoc for the
PropertyPlaceholderConfigurer
for more
information.
![]() | Class name substitution |
---|---|
The <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <value>classpath:com/foo/strategy.properties</value> </property> <property name="properties"> <value>custom.strategy.class=com.foo.DefaultStrategy</value> </property> </bean> <bean id="serviceStrategy" class="${custom.strategy.class}"/> If the class is unable to be resolved at runtime to a valid
class, resolution of the bean will fail once it is about to be
created (which is during the
|
The PropertyOverrideConfigurer
, another
bean factory post-processor, is similar to the
PropertyPlaceholderConfigurer
, but in
contrast to the latter, the original definitions can have default
values or no values at all for bean properties. If an overriding
Properties
file does not have an entry for a
certain bean property, the default context definition is used.
Note that the bean factory definition is
not aware of being overridden, so it is not
immediately obvious when looking at the XML definition file that the
override configurer is being used. In case that there are multiple
PropertyOverrideConfigurer
instances that
define different values for the same bean property, the last one will
win (due to the overriding mechanism).
Properties file configuration lines are expected to be in the format:
beanName.property=value
An example properties file might look like this:
dataSource.driverClassName=com.mysql.jdbc.Driver dataSource.url=jdbc:mysql:mydb
This example file would be usable against a container definition which contains a bean called dataSource, which has driver and url properties.
Note that compound property names are also supported, as long as every component of the path except the final property being overridden is already non-null (presumably initialized by the constructors). In this example...
foo.fred.bob.sammy=123
... the sammy
property of the
bob
property of the fred
property of the foo
bean is being set to the scalar
value 123
.
Note: Specified override values are always literal values; they are not translated into bean references. This also applies when the original value in the XML bean definition specifies a bean reference
With the context
namespace introduced in
Spring 2.5, it is possible to configure property overriding with a
dedicated configuration element:
<context:property-override location="classpath:override.properties"/>
The
org.springframework.beans.factory.FactoryBean
interface is to be implemented by objects that are themselves
factories.
The FactoryBean
interface is a
point of pluggability into the Spring IoC containers instantiation
logic. If you have some 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()
: has to return 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()
: has to return
true
if this
FactoryBean
returns singletons,
false
otherwise
Class getObjectType()
: has to return
either the object type returned by the
getObject()
method or
null
if the type isn't known in advance
The FactoryBean
concept and
interface is used in a number of places within the Spring Framework; at
the time of writing there are over 50 implementations of the
FactoryBean
interface that ship with
Spring itself.
Finally, there is sometimes a need to ask a container for an
actual FactoryBean
instance itself, not
the bean it produces. This may be achieved by prepending the bean id
with '&'
(sans quotes) when calling the
getBean
method of the
BeanFactory
(including
ApplicationContext
). So for a given
FactoryBean
with an id of
myBean
, invoking getBean("myBean")
on the container will return the product of the
FactoryBean
, but invoking
getBean("&myBean")
will return the
FactoryBean
instance itself.