Additional Capabilities of the
ApplicationContext
As was discussed in the chapter introduction, the
org.springframework.beans.factory package provides basic
functionality for managing and manipulating beans, including in a
programmatic way. The org.springframework.context package
adds the ApplicationContext interface, which
extends the BeanFactory interface, in
addition to extending other interfaces to provide additional functionality
in a more application framework-oriented style. Many
people use the ApplicationContext in a
completely declarative fashion, not even creating it programmatically, 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 application.
To enhance BeanFactory functionality in a
more framework-oriented style the context package also provides the
following functionality:
Access to messages in i18n-style, through the
MessageSource interface.
Access to resources, such as URLs and files,
through the ResourceLoader
interface.
Event publication to beans implementing the
ApplicationListener interface, through
the use of the ApplicationEventPublisher
interface.
Loading of multiple (hierarchical) contexts,
allowing each to be focused on one particular layer, such as the web
layer of an application, through the
HierarchicalBeanFactory interface.
Internationalization using
MessageSource
The ApplicationContext interface
extends an interface called MessageSource,
and therefore provides internationalization (i18n) functionality. Spring
also provides the interface
HierarchicalMessageSource, which can resolve
messages hierarchically. Together these interfaces provide the foundation
upon which Spring effects message resolution. The methods defined on these
interfaces include:
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 become 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 preceding
methods are also wrapped in a class named
MessageSourceResolvable, which you can
use with this method.
When an ApplicationContext is loaded,
it automatically searches for a
MessageSource bean defined in the context.
The bean must have the name messageSource. If such a
bean is found, all calls to the preceding methods are delegated to the
message source. If no message source is found, the
ApplicationContext attempts to find a
parent containing a bean with the same name. If it does, it uses that bean
as the MessageSource. If the
ApplicationContext cannot find any source
for messages, an empty DelegatingMessageSource is
instantiated in order to be able to accept calls to the methods defined
above.
Spring provides two MessageSource
implementations, ResourceBundleMessageSource and
StaticMessageSource. Both implement
HierarchicalMessageSource in order to do
nested messaging. The StaticMessageSource is rarely
used but provides programmatic ways to add messages to the source. The
ResourceBundleMessageSource is shown in the
following example:
<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>
In the example it is assumed you have three resource bundles defined
in your classpath called format,
exceptions and windows. Any request
to resolve a message will be handled in the JDK standard way of resolving
messages through ResourceBundles. For the purposes of the example, 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.
A program to execute the MessageSource
functionality is shown in the next example. 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, which exists at the root of
your classpath. The messageSource bean definition
refers to a number of resource bundles through 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.
The next example shows arguments passed to the message lookup; these
arguments will be converted into Strings and inserted into placeholders in
the lookup message.
<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>
<!-- lets 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.
Typically, locale resolution is managed by the surrounding environment
of the application. In this example, the locale against which (British)
messages will be resolved is specified manually.
# 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.
You can also use the MessageSourceAware
interface to acquire a reference to any
MessageSource that has been defined. Any bean that
is defined in an ApplicationContext that implements
the MessageSourceAware interface is injected with
the application context's MessageSource when the
bean is created and configured.
As an alternative to
ResourceBundleMessageSource, Spring 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.
Standard and Custom Events
Event handling in the
ApplicationContext is provided through the
ApplicationEvent class and
ApplicationListener interface. If a bean
that implements the ApplicationListener
interface is deployed into the context, every time an
ApplicationEvent gets published to the
ApplicationContext, that bean is notified.
Essentially, this is the standard Observer design
pattern. Spring provides the following standard events:
Built-in Events
Event
Explanation
ContextRefreshedEvent
Published when the
ApplicationContext is initialized
or refreshed, for example, 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. As long as the context has not been closed, a refresh can
be triggered multiple times, provided that the chosen
ApplicationContext actually
supports such "hot" refreshes. For example,
XmlWebApplicationContext supports hot
refreshes, but GenericApplicationContext
does not.
ContextStartedEvent
Published when the
ApplicationContext is started,
using the start() method on the
ConfigurableApplicationContext
interface. "Started" here means that all
Lifecycle beans receive an explicit
start signal. Typically this signal is used to restart beans after
an explicit stop, but it may also be used to start components that
have not been configured for autostart , for example, components
that have not already started on initialization.
ContextStoppedEvent
Published when the
ApplicationContext is stopped,
using the stop() method on the
ConfigurableApplicationContext
interface. "Stopped" here means that all
Lifecycle beans 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 reaches 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 event is published
after the request is complete. This event is
only applicable to web applications using Spring's
DispatcherServlet.
You can also create and publish your own custom events. This example
demonstrates a simple class that extends Spring's
ApplicationEvent base class:
public class BlackListEvent extends ApplicationEvent {
private final String address;
private final String test;
public BlackListEvent(Object source, String address, String test) {
super(source);
this.address = address;
this.test = test;
}
// accessor and other methods...
}
To publish a custom ApplicationEvent, call the
publishEvent() method on an
ApplicationEventPublisher. Typically this
is done by creating a class that implements
ApplicationEventPublisherAware and
registering it as a Spring bean. The following example demonstrates such a
class:
blackList;
private ApplicationEventPublisher publisher;
public void setBlackList(List blackList) {
this.blackList = blackList;
}
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public void sendEmail(String address, String text) {
if (blackList.contains(address)) {
BlackListEvent event = new BlackListEvent(this, address, text);
publisher.publishEvent(event);
return;
}
]]>// send email...
At configuration time, the Spring container will detect that
EmailService implements
ApplicationEventPublisherAware and will
automatically call
setApplicationEventPublisher(). In reality, the
parameter passed in will be the Spring container itself; you're simply
interacting with the application context via its
ApplicationEventPublisher interface.
To receive the custom ApplicationEvent, create
a class that implements ApplicationListener
and register it as a Spring bean. The following example demonstrates such
a class:
{
private String notificationAddress;
public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}
public void onApplicationEvent(BlackListEvent event) {
]]> // notify appropriate parties via notificationAddress...
Notice that ApplicationListener is
generically parameterized with the type of your custom event,
BlackListEvent. This means that the
onApplicationEvent() method can remain type-safe,
avoiding any need for downcasting. You may register as many event
listeners as you wish, but note that by default event listeners receive
events synchronously. This means the
publishEvent() method blocks until all listeners
have finished processing the event. One advantage of this synchronous and
single-threaded approach is that when a listener receives an event, it
operates inside the transaction context of the publisher if a transaction
context is available. If another strategy for event publication becomes
necessary, refer to the JavaDoc for Spring's
ApplicationEventMulticaster
interface.
The following example demonstrates the bean definitions used to
register and configure each of the classes above:
black@list.org
white@list.org
john@doe.org
]]>
Putting it all together, when the sendEmail()
method of the emailService bean is called, if there are
any emails that should be blacklisted, a custom event of type
BlackListEvent is published. The
blackListNotifier bean is registered as an
ApplicationListener and thus receives the
BlackListEvent, at which point it can notify
appropriate parties.
Spring's eventing mechanism is designed for simple communication
between Spring beans within the same application context. However, for
more sophisticated enterprise integration needs, the
separately-maintained Spring
Integration project provides complete support for building
lightweight, pattern-oriented, event-driven architectures that build upon
the well-known Spring programming model.
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 .
An application context is a
ResourceLoader, which can be used to load
Resources. A
Resource is essentially a more feature rich
version of the JDK class java.net.URL, in fact, the
implementations of the Resource wrap an
instance of java.net.URL where appropriate. A
Resource can 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.
You can configure a bean deployed into the application context to
implement the special callback interface,
ResourceLoaderAware, to be automatically
called back at initialization time with the application context itself
passed in as the ResourceLoader. You can
also expose properties of type Resource, to
be used to access static resources; they will be injected into it like any
other properties. You can 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 when the bean is
deployed.
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. You can also use location paths
(resource strings) with special prefixes to force loading of definitions
from the classpath or a URL, regardless of the actual context type.
Convenient ApplicationContext
instantiation for web applications
You can create ApplicationContext
instances declaratively by using, for example, a
ContextLoader. Of course you can also create
ApplicationContext instances
programmatically by using one of the
ApplicationContext implementations.
The ContextLoader mechanism comes in two
flavors: the ContextLoaderListener and the
ContextLoaderServlet. They have the same
functionality but differ in that the listener version is not reliable in
Servlet 2.3 containers. In the Servlet 2.4 specification, Servlet context
listeners must execute immediately after the Servlet context for the web
application is 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. 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 uses
/WEB-INF/applicationContext.xml as a default. When the
parameter does exist, the listener separates the
String by using predefined delimiters (comma, semicolon and whitespace)
and uses the values as locations where application contexts will be
searched. Ant-style path patterns are supported as well. Examples are
/WEB-INF/*Context.xml for all files with names ending
with "Context.xml", residing in the "WEB-INF" directory, and
/WEB-INF/**/*Context.xml, for all such files in any
subdirectory of "WEB-INF".
You can use ContextLoaderServlet instead of
ContextLoaderListener. The Servlet uses the
contextConfigLocation parameter just as the listener
does.
Deploying a Spring ApplicationContext as a J2EE RAR file
In Spring 2.5 and later, it is possible to deploy a Spring
ApplicationContext as a RAR file, encapsulating the context and all of its
required bean classes and library JARs in a J2EE RAR deployment unit. This
is the equivalent of bootstrapping a standalone ApplicationContext, just
hosted in J2EE environment, being able to access the J2EE servers
facilities. RAR deployment is a more natural alternative to scenario of
deploying a headless WAR file, in effect, a WAR file without any HTTP
entry points that is used only for bootstrapping a Spring
ApplicationContext in a J2EE environment.
RAR deployment is ideal for application contexts that do not need HTTP
entry points but rather consist only of message endpoints and scheduled
jobs. Beans in such a context can use application server resources such as
the JTA transaction manager and JNDI-bound JDBC DataSources and JMS
ConnectionFactory instances, and may also register with the platform's JMX
server - all through Spring's standard transaction management and JNDI and
JMX support facilities. Application components can also interact with the
application server's JCA WorkManager through Spring's
TaskExecutor abstraction.
Check out the JavaDoc of the SpringContextResourceAdapter class for the configuration details
involved in RAR deployment.
For a simple deployment of a Spring ApplicationContext as a
J2EE RAR file: package all application classes into a RAR file,
which is a standard JAR file with a different file extension. Add all
required library JARs into the root of the RAR archive. Add a
"META-INF/ra.xml" deployment descriptor (as shown in
SpringContextResourceAdapters JavaDoc) and the
corresponding Spring XML bean definition file(s) (typically
"META-INF/applicationContext.xml"), and drop the resulting RAR file into
your application server's deployment directory.
Such RAR deployment units are usually self-contained; they do not
expose components to the outside world, not even to other modules of the
same application. Interaction with a RAR-based ApplicationContext
usually occurs through JMS destinations that it shares with other
modules. A RAR-based ApplicationContext may also, for example, schedule
some jobs, reacting to new files in the file system (or the like). If it
needs to allow synchronous access from the outside, it could for example
export RMI endpoints, which of course may be used by other application
modules on the same machine.