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.
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.
Note | |
---|---|
To change the actual bean definition (that is, the recipe that
defines the bean), you instead need to use a
|
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
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
.
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 Chapter 26, Dynamic language support.)
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
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
shown in Section 27.2.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: BeanFactoryPostProcessor
s 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.
Note | |
---|---|
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
Also, |
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.
Note | |
---|---|
As with |
You use the
PropertyPlaceholderConfigurer
to
externalize property values from a bean definition into another
separate file in the standard Java Properties
format. Doing so enables the person deploying an application to
customize environment-specific properties such as database URLs 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. The example shows properties configured
from an external Properties
file. At runtime, a
PropertyPlaceholderConfigurer
is applied to the
metadata that will replace some properties of the DataSource. The
values to replace are specified as 'placeholders' of the form
${property-name} which follows the Ant / Log4J / JSP EL style.
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations" value="classpath:com/foo/jdbc.properties"/> </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
Therefore, the string ${jdbc.username} is replaced at runtime with the value 'sa' and similarly for other placeholder values that match to keys in the property file. The PropertyPlaceholderConfigurer checks for placeholders in most locations of a bean definition and the placeholder prefix and suffix can be customized.
With the context
namespace introduced in
Spring 2.5, it is possible to configure property placeholders with a
dedicated configuration element. You can provide multiple locations as
a comma-separated list in the location
attribute.
<context:property-placeholder location="classpath:com/foo/jdbc.properties"/>
The PropertyPlaceholderConfigurer
does
not look for properties only 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. You can customize this behavior by setting the
systemPropertiesMode
property of the configurer. It
has three values that specify configurer behavior: always override,
never override, and override only if the property
is not found in the properties file specified.
Consult the Javadoc for the
PropertyPlaceholderConfigurer
for more
information.
Class name substitution | |
---|---|
You can use 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 cannot be resolved at runtime to a valid class,
resolution of the bean fails when it is about to be created, which
is during the |
The PropertyOverrideConfigurer
, another
bean factory post-processor, resembles the
PropertyPlaceholderConfigurer
, but
unlike 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 definition is not aware
of being overridden, so it is not immediately obvious from the XML
definition file that the override configurer is used. In case of
multiple PropertyOverrideConfigurer
instances
that define different values for the same bean property, the last one
wins, due to the overriding mechanism.
Properties file configuration lines take this format:
beanName.property=value
For example:
dataSource.driverClassName=com.mysql.jdbc.Driver dataSource.url=jdbc:mysql:mydb
This example file is usable against a container definition that contains a bean called dataSource, which has driver and url properties.
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 set to the scalar value
123
.
Note | |
---|---|
Specified override values are always literal values; they are not translated into bean references. This convention 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"/>
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.