While the beans
package provides basic
functionality for managing and manipulating beans, including in a
programmatic way, the context
package adds the ApplicationContext
interface, 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 instantiate an
ApplicationContext
as part of the normal
startup process of a J2EE web-app. (Of course, it is still possible to
create an ApplicationContext
programmatically.)
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.
Short version: use an
ApplicationContext
unless you have a
really good reason for not doing so. For those of you that are looking
for slightly more depth as to the 'but why' of the above recommendation,
keep reading.
As the ApplicationContext
includes
all functionality of the BeanFactory
, it
is generally recommended that it be used in preference to the
BeanFactory
, except for a few limited
situations such as in an Applet
, where memory
consumption might be critical and a few extra kilobytes might make a
difference. However, for most 'typical' enterprise applications and
systems, the ApplicationContext
is what
you will want to use. Versions of Spring 2.0 and above make
heavy use of the BeanPostProcessor
extension point (to effect proxying and suchlike), and if you are
using just a plain BeanFactory
then a
fair amount of support such as transactions and AOP will not take effect
(at least not without some extra steps on your part), which could be
confusing because nothing will actually be wrong with the
configuration.
Find below a feature matrix that lists what features are provided
by the BeanFactory
and
ApplicationContext
interfaces (and
attendant implementations). (The following sections describe
functionality that ApplicationContext
adds to the basic BeanFactory
capabilities in a lot more depth than the said feature matrix.)
Table 4.5. Feature Matrix
Feature | BeanFactory | ApplicationContext |
---|---|---|
Bean instantiation/wiring | Yes | Yes |
Automatic
| No | Yes |
Automatic
| No | Yes |
Convenient
| No | Yes |
| No | Yes |
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
DelegatingMessageSource
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
HierarchicalMessageSource
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:
<beans> <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basenames"> <list> <value>format</value> <value>exceptions</value> <value>windows</value> </list> </property> </bean> </beans>
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
MessageSource
functionality can be found below.
Remember that all ApplicationContext
implementations are also MessageSource
implementations and so can be cast to the
MessageSource
interface.
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 its 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:
<beans> <!-- this MessageSource is being used in a web application --> <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basename" value="test-messages"/> </bean> <!-- let's inject the above MessageSource into this POJO --> <bean id="example" class="com.foo.Example"> <property name="messages" ref="messageSource"/> </bean> </beans>
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.
Note: As an alternative to
ResourceBundleMessageSource
, Spring also provides
a ReloadableResourceBundleMessageSource
class.
This variant supports the same bundle file format but is more flexible
than the standard JDK based
ResourceBundleMessageSource
implementation. In particular, it allows for reading files
from any Spring resource location (not just from the classpath) and
supports hot reloading of bundle property files (while efficiently
caching them in between). Check out the
ReloadableResourceBundleMessageSource
javadoc for
details.
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 the
following standard events:
Table 4.6. Built-in Events
Event | Explanation |
---|---|
ContextRefreshedEvent | Published when the
ApplicationContext is initialized
or refreshed, e.g. using the refresh()
method on the
ConfigurableApplicationContext
interface. "Initialized" here means that all beans are loaded,
post-processor beans are detected and activated, singletons are
pre-instantiated, and the
ApplicationContext object is
ready for use. A refresh may be triggered multiple times, as
long as the context hasn't been closed - provided that the
chosen ApplicationContext
actually supports such "hot" refreshes (which e.g.
XmlWebApplicationContext does but
GenericApplicationContext
doesn't). |
ContextStartedEvent | Published when the
ApplicationContext is started,
using the start() method on the
ConfigurableApplicationContext
interface. "Started" here means that all
Lifecycle beans will receive an
explicit start signal. This will typically be used for
restarting after an explicit stop, but may also be used for
starting components that haven't been configured for autostart
(e.g. haven't started on initialization already). |
ContextStoppedEvent | Published when the
ApplicationContext is stopped,
using the stop() method on the
ConfigurableApplicationContext
interface. "Stopped" here means that all
Lifecycle beans will receive an
explicit stop signal. A stopped context may be restarted through
a start() call. |
ContextClosedEvent | Published when the
ApplicationContext is closed,
using the close() method on the
ConfigurableApplicationContext
interface. "Closed" here means that all singleton beans are
destroyed. A closed context has reached its end of life; it
cannot be refreshed or restarted. |
RequestHandledEvent | A web-specific event telling all beans that an 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
:
<bean id="emailer" class="example.EmailBean"> <property name="blackList"> <list> <value>[email protected]</value> <value>[email protected]</value> <value>[email protected]</value> </list> </property> </bean> <bean id="blackListListener" class="example.BlackListNotifier"> <property name="notificationAddress" value="[email protected]"/> </bean>
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 event = new BlackListEvent(address, text); ctx.publishEvent(event); return; } // send email... } }
public class BlackListNotifier implements ApplicationListener { private String notificationAddress; public void setNotificationAddress(String notificationAddress) { this.notificationAddress = notificationAddress; } public void onApplicationEvent(ApplicationEvent event) { if (event 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.
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 5, Resources.
An application context is a
ResourceLoader
, able to be used to load
Resource
s. 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 callback 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
ApplicationContext
constructor are
actually resource strings, and in simple form are treated appropriately
to the specific context implementation (
ClassPathXmlApplicationContext
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.
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
mechanism
and its implementations.
The ContextLoader
mechanism comes in two
flavors: the ContextLoaderListener
and the
ContextLoaderServlet
. They both have the same
functionality but differ in that the listener version cannot be reliably
used in Servlet 2.3 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:
<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- or use the ContextLoaderServlet instead of the above listener <servlet> <servlet-name>context</servlet-name> <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> -->
The listener inspects the
'contextConfigLocation'
parameter. If the parameter
does not exist, the listener will use
/WEB-INF/applicationContext.xml
as a default. When it
does exist, it will separate the String using
predefined delimiters (comma, semicolon and whitespace) and use the
values as locations where application contexts will be searched for.
Ant-style path patterns are supported as well: e.g.
/WEB-INF/*Context.xml
(for all files whose name ends
with "Context.xml", residing in the "WEB-INF" directory) or
/WEB-INF/**/*Context.xml
(for all such files in any
subdirectory of "WEB-INF").
The ContextLoaderServlet
can be used
instead of the ContextLoaderListener
. The servlet
will use the 'contextConfigLocation'
parameter just
as the listener does.