- 论坛徽章:
- 0
|
3.5. Customizing the nature of a bean3.5.1. Lifecycle interfaces
The Spring Framework provides several marker interfaces to change the behavior
of your bean in the container; they include
InitializingBeanDisposableBean. Implementing these interfaces will
result in the container calling
afterPropertiesSet() for the former and
destroy() and
for the latter to allow the bean to perform
certain actions upon initialization and destruction.
Internally, the Spring Framework uses
BeanPostProcessorBeanPostProcessor yourself. More information about
this can be found in the section entitled
Section 3.7, “Container extension points”
. implementations to
process any marker interfaces it can find and call the appropriate
methods. If you need custom features or other lifecycle behavior Spring
doesn't offer out-of-the-box, you can implement a
All the different lifecycle marker interfaces are described below.
In one of the appendices, you can find diagram that show how Spring
manages beans and how those lifecycle features change the nature of your
beans and how they are managed.
3.5.1.1. Initialization callbacks
Implementing the
org.springframework.beans.factory.InitializingBean
interface allows a bean to perform initialization work after all necessary
properties on the bean are set by the container. The
InitializingBean interface specifies exactly one
method:
void afterPropertiesSet() throws Exception;
Generally, the use of the InitializingBean
interface can be avoided (and is discouraged since it unnecessarily couples
the code to Spring). A bean definition provides support for a generic
initialization method to be specified. In the case of XML-based configuration
metadata, this is done using the 'init-method' attribute.
For example, the following definition:
public class ExampleBean {
public void init() {
// do some initialization work
}
}
Is exactly the same as...
public class AnotherExampleBean implements InitializingBean {
public void afterPropertiesSet() {
// do some initialization work
}
}
... but does not couple the code to Spring.
3.5.1.2. Destruction callbacks
Implementing the
org.springframework.beans.factory.DisposableBeanDisposableBean interface specifies one
method:
interface allows a bean to get a callback when the container
containing it is destroyed. The
void destroy() throws Exception;
Generally, the use of the
DisposableBean marker interface can be avoided (and
is discouraged since it unnecessarily couples the code to Spring). A
bean definition provides support for a generic destroy method to be
specified. When using XML-based configuration metadata this is done via the
'destroy-method' attribute on the .
For example, the following definition:
public class ExampleBean {
public void cleanup() {
// do some destruction work (like releasing pooled connections)
}
}
Is exactly the same as...
public class AnotherExampleBean implements DisposableBean {
public void destroy() {
// do some destruction work (like releasing pooled connections)
}
}
... but does not couple the code to Spring.
3.5.1.2.1. Default initialization & destroy methods
When you are writing initialization and destroy method callbacks that do not
use the Spring-specific InitializingBean
and DisposableBeaninit(),
initialize(), dispose(), etc. The
names of such lifecycle callback methods are (hopefully!) standardized
across a project so that developers on a team all use the same method
names and thus ensure some level of consistency.
callback interfaces,
one (in the experience of this author) typically finds oneself writing
methods with names such as
The Spring container can now be configured to 'look'
for named initialization and destroy callback method names on
every bean. This means that you as an application developer
can simply write your application classes, use a convention of having an
initialization callback called init(), and then
(without having to configure each and every bean with, in the case of XML-based
configuration, an 'init-method="init"' attribute)
be safe in the knowledge that the Spring IoC container will
call that method when the bean is being created (and in accordance with the
standard lifecycle callback contract described previously).
Let's look at an example to make the use of this feature completely clear.
For the sake of the example, let us say that one of the coding conventions on a
project is that all initialization callback methods are to be named
init() and that destroy callback methods are to be called
destroy(). This leads to classes like so...
public class DefaultBlogService implements BlogService {
private BlogDao blogDao;
public void setBlogDao(BlogDao blogDao) {
this.blogDao = blogDao;
}
// this is (unsurprisingly) the initialization callback method
public void init() {
if (this.blogDao == null) {
throw new IllegalStateException("The [blogDao] property must be set.");
}
}
}default-init-method="init">
Notice the use of the 'default-init-method' attribute on the
top-level element. The presence of this
attribute means that the Spring IoC container will recognize a method called
'init' on beans as being the initialization method callback,
and when a bean is being created and assembled, if the bean's class has such
a method, it will be invoked at the appropriate time.
Destroy method callbacks are configured similarly (in XML that is) using the
'default-destroy-method' attribute on the top-level
element.
The use of this feature can save you the (small) housekeeping chore of specifying
an initialization and destroy method callback on each and every bean, and it is
great for enforcing a consistent naming convention for initialization and destroy
method callbacks (and consistency is something that should always be aimed for).
Consider the case where you have some existing beans where the
underlying classes already have initialization callback methods that
are named at variance with the convention. You can
always override the default by specifying (in XML that is)
the method name using the 'init-method' and
'destroy-method' attributes on the
element itself.
Finally, please be aware that the Spring container guarantees that
a configured initialization callback is called immediately after a bean
has been supplied with all of it's dependencies. This means that the
initialization callback will be called on the raw bean reference, which
means that any AOP interceptors or suchlike that will ultimately
be applied to the bean will not yet be in place. A target bean is fully
created first, then
an AOP proxy (for example) with its interceptor chain is applied. Note that,
if the target bean and the proxy are defined separately, your code can even
interact to the raw target bean, bypassing the proxy. Hence, it would be very
inconsistent to apply the interceptors to the init method, since that would couple
the lifecycle of the target bean with its proxy/interceptors, and leave strange
semantics when talking to the raw target bean directly.
3.5.1.2.2. Shutting down the Spring IoC container gracefully in non-web applications
Note
This next section does not apply to web applications (in case the title of this section
did not make that abundantly clear). Spring's web-based ApplicationContext
implementations already have code in place to handle shutting down the Spring
IoC container gracefully when the relevant web application is being shutdown.
If you are using Spring's IoC container in a non-web application environment, for
example in a rich client desktop environment, and you want the container to shutdown
gracefully and call the relevant destroy callbacks on your singleton beans, you will
need to register a shutdown hook with the JVM. This is quite easy to do (see below),
and will ensure that your Spring IoC container shuts down gracefully and that all
resources held by your singletons are released (of course it is still up to you to both
configure the destroy callbacks for your singletons and implement such
destroy callbacks correctly).
So to register a shutdown hook that enables the graceful shutdown of the relevant
Spring IoC container, you simply need to call the
registerShutdownHook() method that is declared on the
AbstractApplicationContext class. To wit...
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public final class Boot {
public static void main(final String[] args) throws Exception {
AbstractApplicationContext ctx
= new ClassPathXmlApplicationContext(new String []{"beans.xml"});
// add a shutdown hook for the above context...
ctx.registerShutdownHook();
// app runs here...
// main method exits, hook is called prior to the app shutting down...
}
}3.5.2. Knowing who you are3.5.2.1.
BeanFactoryAware
A class which implements the
org.springframework.beans.factory.BeanFactoryAware
interface is provided with a reference to the BeanFactory that created
it, when it is created by that BeanFactory.
public interface BeanFactoryAware {
void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}
This allows beans to manipulate the BeanFactory
that created them programmatically, through the BeanFactory
interface, or by casting the reference to a known subclass of this
which exposes additional functionality. Primarily this would consist
of programmatic retrieval of other beans. While there are cases when
this capability is useful, it should generally be avoided, since it
couples the code to Spring, and does not follow the Inversion of
Control style, where collaborators are provided to beans as properties.
An alternative option that is equivalent in effect to the
BeanFactoryAware-based approach is to use the
org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean.
(It should be noted that this approach still does not reduce the coupling to Spring,
but it does not violate the central principle of IoC as much as the
BeanFactoryAware-based approach.)
The ObjectFactoryCreatingFactoryBean is a
FactoryBean
ObjectFactoryCreatingFactoryBean class
does itself implement the BeanFactoryAware interface;
what client beans are actually injected with is an instance of the
ObjectFactory interface. This is a Spring-specific
interface (and hence there is still no total decoupling from Spring), but clients
can then use the ObjectFactory's
getObject()ObjectFactoryBeanFactory to actually
lookup a bean by name). All that you need to do is supply
the ObjectFactoryCreatingFactoryBean with the name of the
bean that is to be looked up. Let's look at an example:
implementation that returns a reference to an object (factory) that can in turn be used to
effect a bean lookup. The method to effect the bean lookup (under the hood the
implementation instance that is returned
simply delegates down to a
package x.y;
public class NewsFeed {
private String news;
public void setNews(String news) {
this.news = news;
}
public String getNews() {
return this.toString() + ": '" + news + "'";
}
}package x.y;
import org.springframework.beans.factory.ObjectFactory;
public class NewsFeedManager {
private ObjectFactory factory;
public void setFactory(ObjectFactory factory) {
this.factory = factory;
}
public void printNews() {
// here is where the lookup is performed; note that there is no
// need to hardcode the name of the bean that is being looked up...
NewsFeed news = (NewsFeed) factory.getObject();
System.out.println(news.getNews());
}
}
Find below the XML configuration to wire together the above classes
using the ObjectFactoryCreatingFactoryBean approach.
And here is a small driver program to test the fact that new (prototype)
instances of the newsFeed bean are actually being returned for
each call to the injected ObjectFactory inside the
NewsFeedManager's printNews() method.
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import x.y.NewsFeedManager;
public class Main {
public static void main(String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
NewsFeedManager manager = (NewsFeedManager) ctx.getBean("newsFeedManager");
manager.printNews();
manager.printNews();
}
}
The output from running the above program will look like so (results will of course vary
on your machine).
x.y.NewsFeed@1292d26: '... that's fit to print!'
x.y.NewsFeed@5329c5: '... that's fit to print!'3.5.2.2. BeanNameAware
If a bean implements the
org.springframework.beans.factory.BeanNameAware
interface and is deployed in a BeanFactory, the
BeanFactory will call the bean through this
interface to inform the bean of the id it was deployed
under. The callback will be invoked after population of normal bean properties
but before an initialization callback like InitializingBean's
afterPropertiesSet or a custom init-method.
3.6. Bean definition inheritance
A bean definition potentially contains a large amount of
configuration information, including container specific information (for example
initialization method, static factory method name, and so forth) and constructor
arguments and property values. A child bean definition is a bean
definition that inherits configuration data from a parent definition. It
is then able to override some values, or add others, as needed. Using
parent and child bean definitions can potentially save a lot of typing.
Effectively, this is a form of templating.
When working with a BeanFactory programmatically, child bean
definitions are represented by the ChildBeanDefinition
class. Most users will never work with them on this level, instead
configuring bean definitions declaratively in something like the
XmlBeanFactory. When using XML-based configuration metadata a child bean
definition is indicated simply by using the 'parent'
attribute, specifying the parent bean as the value of this attribute.
parent="inheritedTestBean" init-method="initialize">
A child bean definition will use the bean class from the parent
definition if none is specified, but can also override it. In the latter
case, the child bean class must be compatible with the parent, that is it
must accept the parent's property values.
A child bean definition will inherit constructor argument values,
property values and method overrides from the parent, with the option to
add new values. If any init-method, destroy-method and/or static
factory method settings are specified, they will override the corresponding parent
settings.
The remaining settings will always be taken
from the child definition: depends on,
autowire mode, dependency check,
singleton, scope,
lazy init.
Note that in the example above, we have explicitly marked the parent
bean definition as abstract by using the abstract
attribute. In the case that the parent definition does not specify a
class, and so explicitly marking the parent bean definition as
abstract is required:
1 from the parent bean definition-->
The parent bean cannot get instantiated on its own since it is
incomplete, and it is also explicitly marked as abstract.
When a definition is defined to be abstract like this,
it is usable only as a pure template bean definition that will serve as a
parent definition for child definitions. Trying to use such an
abstract parent bean on its own (by referring to it as
a ref property of another bean, or doing an explicit
getBean() call with the parent bean id), will
result in an error. Similarly, the container's internal
preInstantiateSingletons() method will completely
ignore bean definitions which are defined as abstract.
Note
ApplicationContexts (but not
BeanFactories) will by default pre-instantiate
all singletons. Therefore it is important (at least for singleton beans)
that if you have a (parent) bean definition which you intend to use only
as a template, and this definition specifies a class, you must make sure
to set the 'abstract' attribute to 'true',
otherwise the application context will actually (attempt to) pre-instantiate
the abstract bean.
3.7. Container extension points
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.
3.7.1. Customizing beans using BeanPostProcessors
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
OrderedBeanPostProcessor and Ordered
interfaces for more details. interface too); consult the Javadocs for the
Note
BeanPostProcessors operate on bean (or object)
instances; that is to say, the Spring IoC container will
have instantiated a bean instance for you, and then
BeanPostProcessors get a chance to do their stuff.
If you want to change the actual bean definition (that is the recipe that
defines the bean), then you rather need to use a
BeanFactoryPostProcessor
Section 3.7.2, “Customizing configuration metadata with BeanFactoryPostProcessors”
. (described below in the
section entitled
Also, BeanPostProcessors 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 stuff on the beans in that container. Beans that
are defined in another container will not be post-processed by
BeanPostProcessors in another container, even if both containers
are part of the same hierarchy.
The org.springframework.beans.factory.config.BeanPostProcessorbefore any container
initialization methods (such as afterPropertiesSet
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 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 marker 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 BeanPostProcessorspecial, and so they are treated differently
by the container. All BeanPostProcessors
and their directly referenced beans will be instantiated
on startup, as part of the special startup phase of the
ApplicationContext, then
all those BeanPostProcessors will be registered
in a sorted fashion - and applied to all further beans. Since AOP
auto-proxying is implemented as a BeanPostProcessor
itself, no BeanPostProcessors or directly
referenced beans are eligible for auto-proxying (and thus will not have
aspects 'woven' into them.
interface are
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.
3.7.1.1. Example: Hello World, BeanPostProcessor-style
This first example is hardly compelling, but serves to illustrate basic
usage. All we are going to do is code a custom BeanPostProcessortoString()
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.
implementation that simply invokes the
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;
}
}
BeanPostProcessor implementation will output the fact to the system console
-->
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 24, 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 : org.springframework.scripting.groovy.GroovyMessenger@272961
org.springframework.scripting.groovy.GroovyMessenger@2729613.7.1.2. Example: The RequiredAnnotationBeanPostProcessor
Using marker 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 25.3.1, “@Required”
BeanPostProcessor which demonstrates the usage of a
custom 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.
3.7.2. Customizing configuration metadata with BeanFactoryPostProcessors
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; that is to say,
the Spring IoC container will allow BeanFactoryPostProcessors to read the
configuration metadata and potentially change it before the
container has actually instantied any other beans.
You can configure multiple BeanFactoryPostProcessors if you wish.
You can control the order in which these BeanFactoryPostProcessors'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 Javadocs for the BeanFactoryPostProcessor
and Ordered interfaces for more details.
execute by setting the
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 BeanPostProcessor
(described above in the section entitled
Section 3.7.1, “Customizing beans using BeanPostProcessors”
.
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 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
PropertyResourceConfigurer and
PropertyPlaceholderConfigurer, both described below,
and BeanNameAutoProxyCreator, which is very useful for wrapping
other beans transactionally or with any other kind of proxy, as described
later in this manual. The BeanFactoryPostProcessor
can be used to add custom property editors.
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 BeanPostProcessors, you typically
don't want to have BeanFactoryPostProcessors
marked as being lazily-initialized. If they are marked as such, then the Spring
container will never instantiate them, and thus they won't get a chance
to apply their custom logic. If you are using the
'default-lazy-init'BeanFactoryPostProcessor bean definitions with
'lazy-init="false"'.
attribute on the declaration of your
element, be sure to mark your various
3.7.2.1. Example: the PropertyPlaceholderConfigurer
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
enviroment-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:
classpath:com/foo/jdbc.properties
${jdbc.driverClassName}"/>
${jdbc.url}"/>
${jdbc.username}"/>
${jdbc.password}"/>
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
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 systemPropertiesModenever 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
PropertiesPlaceholderConfigurer for more information.
property of the configurer. It has three values, one to tell the configurer
to always override, one to let it
Class name substitution
The PropertyPlaceholderConfigurer
can be used to substitute class names, which is sometimes useful
when you have to pick a particular implementation class at runtime.
For example:
classpath:com/foo/strategy.properties
custom.strategy.class=com.foo.DefaultStrategy
If the class is unable to be resolved at runtime to a valid class,
resolution of the the bean will fail once it is about to be created
(which is during the preInstantiateSingletons()ApplicationContext for a
non-lazy-init bean.)
phase of an
3.7.2.2. Example: the PropertyOverrideConfigurer
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.
3.7.3. Customizing instantiation logic using FactoryBeans
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 FactoryBeanfalse otherwise
returns singletons,
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.
3.8. The ApplicationContext
While the beans package provides basic
functionality for managing and manipulating beans, often in a programmatic
way, the context package adds
ApplicationContext
,
which enhances BeanFactory functionality in a more
framework-oriented style. Many users will use
ApplicationContext in a completely declarative fashion,
not even having to create it manually, but instead relying on support classes such as
ContextLoader to automatically start an ApplicationContext as part of the
normal startup process of a J2EE web-app. Of course, it is still possible
to programmatically create an ApplicationContext.
The basis for the context package is the
ApplicationContext interface, located in the
org.springframework.context package. Deriving from the
BeanFactory interface, it provides all the functionality of
BeanFactory. To allow working in a more framework-oriented
fashion, using layering and hierarchical contexts, the context package also provides the
following functionality:
MessageSource, providing access to
messages in i18n-style
Access to resources, such as URLs and files
Event propagation to beans implementing
the ApplicationListener interface
Loading of multiple (hierarchical)
contexts, allowing each to be focused on one particular
layer, for example the web layer of an application
As the ApplicationContext includes all functionality of the
BeanFactory, it is generally recommended that it be used over the
BeanFactory, except for a few limited situations such as perhaps in an
Applet, where memory consumption might be critical, and a few extra
kilobytes might make a difference. The following sections describe
functionality that ApplicationContext adds to basic BeanFactory
capabilities.
3.8.1. Internationalization using MessageSources
The ApplicationContext interface extends an interface called
MessageSource, and therefore provides messaging (i18n
or internationalization) functionality. Together with the
HierarchicalMessageSource, capable of resolving
hierarchical messages, these are the basic interfaces Spring provides to
do message resolution. Let's quickly review the methods defined there:
String getMessage(String code, Object[] args,
String default, Locale loc): the basic method used to
retrieve a message from the MessageSource. When no message is
found for the specified locale, the default message is used. Any
arguments passed in are used as replacement values, using the
MessageFormat functionality provided by the
standard library.
String getMessage(String code, Object[] args,
Locale loc): essentially the same as the previous
method, but with one difference: no default message can be
specified; if the message cannot be found, a
NoSuchMessageException is thrown.
String getMessage(MessageSourceResolvable
resolvable, Locale locale): all properties used in the
methods above are also wrapped in a class named
MessageSourceResolvable, which you can use via
this method.
When an ApplicationContext gets loaded, it automatically searches
for a MessageSource bean defined in the context. The bean has to have
the name messageSource. If such a bean is found, all
calls to the methods described above will be delegated to the message
source that was found. If no message source was found, the
ApplicationContext attempts to see if it has a parent containing a bean
with the same name. If so, it uses that bean as the MessageSource. If it
can't find any source for messages, an empty
StaticMessageSource will be instantiated in order to
be able to accept calls to the methods defined above.
Spring currently provides two MessageSource
implementations. These are the
ResourceBundleMessageSource and the
StaticMessageSource. Both implement
NestingMessageSource in order to do nested messaging.
The StaticMessageSource is hardly ever used but provides programmatic
ways to add messages to the source. The ResourceBundleMessageSource is
more interesting and is the one we will provide an example for:
format
exceptions
windows
This assumes you have three resource bundles defined on your
classpath called format,
exceptions and windows. Using the
JDK standard way of resolving messages through ResourceBundles, any
request to resolve a message will be handled. For the purposes of the example,
lets assume the contents of two of the above resource bundle files are...
# in 'format.properties'
message=Alligators rock!# in 'exceptions.properties'
argument.required=The '{0}' argument is required.
Some (admittedly trivial) driver code to exercise the
MessageSourceApplicationContextMessageSource implementations and so can be cast to the
MessageSource interface. functionality can be found below. Remember
that all implementations are also
public static void main(String[] args) {
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
String message = resources.getMessage("message", null, "Default", null);
System.out.println(message);
}
The resulting output from the above program will be...
Alligators rock!
So to summarize, the MessageSource is defined in a file
called 'beans.xml' (this file exists at the root of your classpath).
The 'messageSource' bean definition refers to a number
of resource bundles via it's basenames property; the three files
that are passed in the list to the basenames property exist as files
at the root of your classpath (and are called
format.properties, exceptions.properties,
and windows.properties respectively).
Lets look at another example, and this time we will look at passing arguments
to the message lookup; these arguments will be converted into strings and inserted
into placeholders in the lookup message. This is perhaps best explained with an
example:
MessageSource is being used in a web application -->
MessageSource into this POJO -->
public class Example {
private MessageSource messages;
public void setMessages(MessageSource messages) {
this.messages = messages;
}
public void execute() {
String message = this.messages.getMessage("argument.required",
new Object [] {"userDao"}, "Required", null);
System.out.println(message);
}
}
The resulting output from the invocation of the execute()
method will be...
The 'userDao' argument is required.
With regard to internationalization (i18n), Spring's various
MessageResource implementations follow the same locale resolution
and fallback rules as the standard JDK ResourceBundle. In short, and continuing with
the example 'messageSource' defined previously, if you want to resolve
messages against the British (en-GB) locale, you would create files called
format_en_GB.properties, exceptions_en_GB.properties,
and windows_en_GB.properties respectively.
Locale resolution is typically going to be managed by the surrounding environment
of the application. For the purpose of this example though, we'll just manually
specify the locale that we want to resolve our (British) messages against.
# in 'exceptions_en_GB.properties'
argument.required=Ebagum lad, the '{0}' argument is required, I say, required.public static void main(final String[] args) {
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
String message = resources.getMessage("argument.required",
new Object [] {"userDao"}, "Required", Locale.UK);
System.out.println(message);
}
The resulting output from the running of the above program will be...
Ebagum lad, the 'userDao' argument is required, I say, required.
The MessageSourceAware interface can also be used to acquire
a reference to any MessageSource that has been defined. Any bean
that is defined in an ApplicationContext that implements the
MessageSourceAware interface will be injected with the
application context's MessageSource when it (the bean) is being
created and configured.
3.8.2. Events
Event handling in the ApplicationContext is provided
through the ApplicationEvent class and
ApplicationListener interface. If a bean which
implements the ApplicationListener interface is
deployed into the context, every time an ApplicationEvent
gets published to the ApplicationContext, that
bean will be notified. Essentially, this is the standard Observer
design pattern. Spring provides three standard events:
Table 3.5. Built-in Events
EventExplanationContextRefreshedEvent
Published when the
ApplicationContext is
initialized or refreshed. Initialized here means that all
beans are loaded, singletons are pre-instantiated and the
ApplicationContext is ready
for use.
ContextClosedEvent
Published when the ApplicationContext is closed,
using the close() method on the
ApplicationContext. Closed here means
that singleton beans (only!) are destroyed.
RequestHandledEvent
A web-specific event telling all beans that a HTTP
request has been serviced (this will be published
after the request has been finished).
Note that this event is only applicable for web applications
using Spring's DispatcherServlet.
Implementing custom events can be done as well. Simply call the
publishEvent() method on the ApplicationContext,
specifying a parameter which is an instance of your custom event class
implementing ApplicationEvent. Event listeners receive events
synchronously. This means the publishEvent() method blocks until all
listeners have finished processing the event (it is possible to supply
an alternate event publishing strategy via a
ApplicationEventMulticaster implementation).
Furthermore, when a listener receives an event it operates inside the transaction context of
the publisher, if a transaction context is available.
Let's look at an example. First, the ApplicationContext:
black@list.org
white@list.org
john@doe.org
Now, let's look at the actual classes:
public class EmailBean implements ApplicationContextAware {
private List blackList;
private ApplicationContext ctx;
public void setBlackList(List blackList) {
this.blackList = blackList;
}
public void setApplicationContext(ApplicationContext ctx) {
this.ctx = ctx;
}
public void sendEmail(String address, String text) {
if (blackList.contains(address)) {
BlackListEvent evt = new BlackListEvent(address, text);
ctx.publishEvent(evt);
return;
}
// send email...
}
}public class BlackListNotifier implements ApplicationListener {
private String notificationAddress;
public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}
public void onApplicationEvent(ApplicationEvent evt) {
if (evt instanceof BlackListEvent) {
// notify appropriate person...
}
}
}
Of course, this particular example could probably be
implemented in better ways (perhaps by using AOP features), but it
should be sufficient to illustrate the basic event mechanism.
3.8.3. Convenient access to low-level resources
For optimal usage and understanding of application contexts, users
should generally familiarize themselves with Spring's
Resource abstraction, as described
in the chapter entitled
Chapter 4, Resources
.
An application context is a ResourceLoader,
able to be used to load Resources. A
Resource is essentially a
java.net.URL on steroids (in fact, it just wraps and
uses a URL where appropriate), which can be used to obtain low-level
resources from almost any location in a transparent fashion, including
from the classpath, a filesystem location, anywhere describable with a
standard URL, and some other variations. If the resource location string
is a simple path without any special prefixes, where those resources
come from is specific and appropriate to the actual application context
type.
A bean deployed into the application context may implement the
special marker interface, ResourceLoaderAware, to be
automatically called back at initialization time with the application
context itself passed in as the ResourceLoader.
A bean may also expose properties of type
Resource, to be used to access static resources, and
expect that they will be injected into it like any other properties. The
person deploying the bean may specify those Resource
properties as simple String paths, and rely on a special JavaBean
PropertyEditor that is automatically registered
by the context, to convert those text strings to actual Resource
objects.
The location path or paths supplied to an ApplicationContextClassPathXmlApplicationContext treats a simple location path as a
classpath location), but may also be used with special prefixes to force
loading of definitions from the classpath or a URL, regardless of the
actual context type.
constructor are actually resource strings, and in simple form are
treated appropriately to the specific context implementation (
3.8.4. Convenient ApplicationContext instantiation for web applications
As opposed to the BeanFactory, which will
often be created programmatically, ApplicationContext
instances can be created declaratively using for example a
ContextLoader. Of course you can also create
ApplicationContext instances programmatically
using one of the ApplicationContext implementations.
First, let's examine the ContextLoader interface
and its implementations.
The ContextLoader interface has two
implementations: the ContextLoaderListener and the
ContextLoaderServlet. They both have the same
functionality but differ in that the listener version cannot be used in Servlet
2.2 compatible containers. Since the Servlet 2.4 specification, servlet context
listeners are required to execute immediately after the servlet context for the
web application has been created and is available to service the first request
(and also when the servlet context is about to be shut down): as such a
servlet context listener is an ideal place to initialize the Spring
ApplicationContext. It is up to you
as to which one you use, but all things being equal you should probably
prefer ContextLoaderListener; for more information on
compatibility, have a look at the Javadoc for the
ContextLoaderServlet.
You can register an ApplicationContext using the
ContextLoaderListener as follows:
contextConfigLocation
/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml
org.springframework.web.context.ContextLoaderListener
ContextLoaderServlet instead of the above listener
context
org.springframework.web.context.ContextLoaderServlet
1
-->
The listener inspects the
contextConfigLocation parameter. If it doesn't exist,
it'll use /WEB-INF/applicationContext.xml as a default.
When it does exist, it'll separate the String using
predefined delimiters (comma, semi-colon and whitespace) and use the values as
locations where application contexts will be searched for. The
ContextLoaderServlet can be used instead of the
ContextLoaderListener. The servlet will use the
'contextConfigLocation' parameter just as the listener does.
3.9. Glue code and the evil singleton
The majority of the code inside an application is best written in a
DI style, where that code is served out of a Spring IoC container, has its own
dependencies supplied by the container when it is created, and is
completely unaware of the container. However, for the small glue layers of
code that are sometimes needed to tie other code together, there is
sometimes a need for singleton (or quasi-singleton) style access to a
Spring IoC container. For example,
third party code may try to construct new objects directly (Class.forName()
style), without the ability to force it to get these objects out of a
Spring IoC container. If the object constructed by the
third party code is just a small stub or proxy, which then uses a singleton style access to a
Spring IoC container to get a real object to delegate to, then inversion of control has
still been achieved for the majority of the code (the object coming out of the
container); thus most code is still unaware of the container or how it is accessed, and
remains uncoupled from other code, with all ensuing benefits. EJBs may also use this stub/proxy
approach to delegate to a plain Java implementation object, coming out of
a Spring IoC container. While the Spring IoC container itself ideally does not have to be a
singleton, it may be unrealistic in terms of memory usage or initialization times (when
using beans in the Spring IoC container such as a Hibernate
SessionFactory) for each bean to use its own, non-singleton
Spring IoC container.
As another example, in a complex J2EE apps with multiple layers
(various JAR files, EJBs, and WAR files packaged as an EAR),
with each layer having its own Spring IoC container definition
(effectively forming a hierarchy), the preferred approach when
there is only one web-app (WAR) in the top hierarchy is to simply
create one composite Spring IoC container from the multiple XML definition
files from each layer. All of the various Spring IoC container implementations
may be constructed from multiple definition files in this fashion.
However, if there are multiple sibling web-applications at the root
of the hierarchy, it is problematic to create a
Spring IoC container for each web-application
which consists of mostly identical bean definitions from lower layers,
as there may be issues due to increased memory usage, issues with
creating multiple copies of beans which take a long time to initialize
(e.g. a Hibernate SessionFactory), and
possible issues due to side-effects. As an alternative, classes such as
ContextSingletonBeanFactoryLocator
or
SingletonBeanFactoryLocator
may be used to demand-load multiple hierarchical (that is one container is the parent of
another) Spring IoC container instances in a singleton fashion,
which may then be used as the parents of the web-application Spring IoC
container instances. The result is that bean definitions for lower layers are
loaded only as needed, and loaded only once.
3.9.1. Using the Singleton-helper classes
You can see a detailed example of their usage in
SingletonBeanFactoryLocator
and
ContextSingletonBeanFactoryLocator
by viewing their respective Javadocs.
As mentioned in the
chapter on EJBs
, the
Spring convenience base classes for EJBs normally use a non-singleton
BeanFactoryLocatorSingletonBeanFactoryLocator
and ContextSingletonBeanFactoryLocator if there is
a need. implementation, which is
easily replaced by the use of
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/24141/showart_324455.html |
|