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 implements 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.
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.
Note | |
---|---|
As an alternative to
|
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:
Table 3.6. 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 implement custom events. Simply call the
publishEvent()
method on the
ApplicationContext
, specifying a
parameter that is an instance of your custom event class that implements
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 through
an ApplicationEventMulticaster
implementation). Furthermore, when a listener receives an event, it
operates inside the transaction context of the publisher, if a
transaction context is available.
This example shows the bean definitions used to configure an
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>
This example shows the implementation of the classes refered to in the previous bean definitions:
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... } } }
When the sendEmail method is called, if there are any emails that should be blacklisted, a custom event of the type BlackListEvent is published to the application context. The BlackListNotifier class which implements the interface ApplicationListener is registered as a subscriber to the application context and will receive the BlackListEvent. In order to access properties specific to BlackListEvent, the listener must perform a downcast.
For optimal usage and understanding of application contexts, users
should generally familiarize themselves with Spring's
Resource
abstraction, as described in the
chapter Chapter 4, Resources.
An application context is a
ResourceLoader
, which can be used to load
Resource
s. 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.
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.
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
SpringContextResourceAdapter
s 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.
Note | |
---|---|
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. |