This version is still in development and is not considered stable yet. For the latest stable version, please use Spring Framework 6.2.0! |
Additional Capabilities of the ApplicationContext
As 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 Jakarta EE 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, namely to beans that implement the
ApplicationListener
interface, through the use of theApplicationEventPublisher
interface. -
Loading of multiple (hierarchical) contexts, letting each 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
HierarchicalMessageSource
interface, 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 theMessageSource
. When no message is found for the specified locale, the default message is used. Any arguments passed in become replacement values, using theMessageFormat
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, aNoSuchMessageException
is thrown. -
String getMessage(MessageSourceResolvable resolvable, Locale locale)
: All properties used in the preceding methods are also wrapped in a class namedMessageSourceResolvable
, 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 three MessageSource
implementations, ResourceBundleMessageSource
, ReloadableResourceBundleMessageSource
and StaticMessageSource
. All of them implement HierarchicalMessageSource
in order to do nested
messaging. The StaticMessageSource
is rarely used but provides programmatic ways to
add messages to the source. The following example shows ResourceBundleMessageSource
:
<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>
The example assumes that you have three resource bundles called format
, exceptions
and windows
defined in your classpath. Any request to resolve a message is
handled in the JDK-standard way of resolving messages through ResourceBundle
objects. For the
purposes of the example, assume the contents of two of the above resource bundle files
are as follows:
# in format.properties
message=Alligators rock!
# in exceptions.properties
argument.required=The {0} argument is required.
The next example shows a program to run the MessageSource
functionality.
Remember that all ApplicationContext
implementations are also MessageSource
implementations and so can be cast to the MessageSource
interface.
-
Java
-
Kotlin
public static void main(String[] args) {
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
String message = resources.getMessage("message", null, "Default", Locale.ENGLISH);
System.out.println(message);
}
fun main() {
val resources = ClassPathXmlApplicationContext("beans.xml")
val message = resources.getMessage("message", null, "Default", Locale.ENGLISH)
println(message)
}
The resulting output from the above program is as follows:
Alligators rock!
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 are
converted into String
objects 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="exceptions"/>
</bean>
<!-- lets inject the above MessageSource into this POJO -->
<bean id="example" class="com.something.Example">
<property name="messages" ref="messageSource"/>
</bean>
</beans>
-
Java
-
Kotlin
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", Locale.ENGLISH);
System.out.println(message);
}
}
class Example {
lateinit var messages: MessageSource
fun execute() {
val message = messages.getMessage("argument.required",
arrayOf("userDao"), "Required", Locale.ENGLISH)
println(message)
}
}
The resulting output from the invocation of the execute()
method is as follows:
The userDao argument is required.
With regard to internationalization (“i18n”), Spring’s various MessageSource
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 the following example, the locale against which (British) messages are resolved is specified manually:
# in exceptions_en_GB.properties argument.required=Ebagum lad, the ''{0}'' argument is required, I say, required.
-
Java
-
Kotlin
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);
}
fun main() {
val resources = ClassPathXmlApplicationContext("beans.xml")
val message = resources.getMessage("argument.required",
arrayOf("userDao"), "Required", Locale.UK)
println(message)
}
The resulting output from the running of the above program is as follows:
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.
Because Spring’s MessageSource is based on Java’s ResourceBundle , it does not merge
bundles with the same base name, but will only use the first bundle found.
Subsequent message bundles with the same base name are ignored.
|
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 only from the classpath) and supports hot
reloading of bundle property files (while efficiently caching them in between).
See the ReloadableResourceBundleMessageSource
javadoc for details.
|
Standard and Custom Events
Event handling in the ApplicationContext
is provided through the ApplicationEvent
class and the 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.
As of Spring 4.2, the event infrastructure has been significantly improved and offers
an annotation-based model as well as the
ability to publish any arbitrary event (that is, an object that does not necessarily
extend from ApplicationEvent ). When such an object is published, we wrap it in an
event for you.
|
The following table describes the standard events that Spring provides:
Event | Explanation |
---|---|
|
Published when the |
|
Published when the |
|
Published when the |
|
Published when the |
|
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 that use Spring’s |
|
A subclass of |
You can also create and publish your own custom events. The following example shows a
simple class that extends Spring’s ApplicationEvent
base class:
-
Java
-
Kotlin
public class BlockedListEvent extends ApplicationEvent {
private final String address;
private final String content;
public BlockedListEvent(Object source, String address, String content) {
super(source);
this.address = address;
this.content = content;
}
// accessor and other methods...
}
class BlockedListEvent(source: Any,
val address: String,
val content: String) : ApplicationEvent(source)
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 shows such a class:
-
Java
-
Kotlin
public class EmailService implements ApplicationEventPublisherAware {
private List<String> blockedList;
private ApplicationEventPublisher publisher;
public void setBlockedList(List<String> blockedList) {
this.blockedList = blockedList;
}
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public void sendEmail(String address, String content) {
if (blockedList.contains(address)) {
publisher.publishEvent(new BlockedListEvent(this, address, content));
return;
}
// send email...
}
}
class EmailService : ApplicationEventPublisherAware {
private lateinit var blockedList: List<String>
private lateinit var publisher: ApplicationEventPublisher
fun setBlockedList(blockedList: List<String>) {
this.blockedList = blockedList
}
override fun setApplicationEventPublisher(publisher: ApplicationEventPublisher) {
this.publisher = publisher
}
fun sendEmail(address: String, content: String) {
if (blockedList!!.contains(address)) {
publisher!!.publishEvent(BlockedListEvent(this, address, content))
return
}
// send email...
}
}
At configuration time, the Spring container detects that EmailService
implements
ApplicationEventPublisherAware
and automatically calls
setApplicationEventPublisher()
. In reality, the parameter passed in is the Spring
container itself. You are interacting with the application context through its
ApplicationEventPublisher
interface.
To receive the custom ApplicationEvent
, you can create a class that implements
ApplicationListener
and register it as a Spring bean. The following example
shows such a class:
-
Java
-
Kotlin
public class BlockedListNotifier implements ApplicationListener<BlockedListEvent> {
private String notificationAddress;
public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}
public void onApplicationEvent(BlockedListEvent event) {
// notify appropriate parties via notificationAddress...
}
}
class BlockedListNotifier : ApplicationListener<BlockedListEvent> {
lateinit var notificationAddress: String
override fun onApplicationEvent(event: BlockedListEvent) {
// notify appropriate parties via notificationAddress...
}
}
Notice that ApplicationListener
is generically parameterized with the type of your custom event (BlockedListEvent
in the preceding example).
This means that the onApplicationEvent()
method can remain type-safe, avoiding any need for downcasting.
You can register as many event listeners as you wish, but note that, by default, event listeners receive events synchronously.
This means that 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, e.g. asynchronous event processing by default,
see the javadoc for Spring’s ApplicationEventMulticaster
interface
and SimpleApplicationEventMulticaster
implementation
for configuration options which can be applied to a custom "applicationEventMulticaster" bean definition.
In these cases, ThreadLocals and logging context are not propagated for the event processing.
See the @EventListener
Observability section
for more information on Observability concerns.
The following example shows the bean definitions used to register and configure each of the classes above:
<bean id="emailService" class="example.EmailService">
<property name="blockedList">
<list>
<value>[email protected]</value>
<value>[email protected]</value>
<value>[email protected]</value>
</list>
</property>
</bean>
<bean id="blockedListNotifier" class="example.BlockedListNotifier">
<property name="notificationAddress" value="[email protected]"/>
</bean>
<!-- optional: a custom ApplicationEventMulticaster definition -->
<bean id="applicationEventMulticaster" class="org.springframework.context.event.SimpleApplicationEventMulticaster">
<property name="taskExecutor" ref="..."/>
<property name="errorHandler" ref="..."/>
</bean>
Putting it all together, when the sendEmail()
method of the emailService
bean is
called, if there are any email messages that should be blocked, a custom event of type
BlockedListEvent
is published. The blockedListNotifier
bean is registered as an
ApplicationListener
and receives the BlockedListEvent
, 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. |
Annotation-based Event Listeners
You can register an event listener on any method of a managed bean by using the
@EventListener
annotation. The BlockedListNotifier
can be rewritten as follows:
-
Java
-
Kotlin
public class BlockedListNotifier {
private String notificationAddress;
public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}
@EventListener
public void processBlockedListEvent(BlockedListEvent event) {
// notify appropriate parties via notificationAddress...
}
}
class BlockedListNotifier {
lateinit var notificationAddress: String
@EventListener
fun processBlockedListEvent(event: BlockedListEvent) {
// notify appropriate parties via notificationAddress...
}
}
The method signature once again declares the event type to which it listens, but, this time, with a flexible name and without implementing a specific listener interface. The event type can also be narrowed through generics as long as the actual event type resolves your generic parameter in its implementation hierarchy.
If your method should listen to several events or if you want to define it with no parameter at all, the event types can also be specified on the annotation itself. The following example shows how to do so:
-
Java
-
Kotlin
@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
// ...
}
@EventListener(ContextStartedEvent::class, ContextRefreshedEvent::class)
fun handleContextStart() {
// ...
}
It is also possible to add additional runtime filtering by using the condition
attribute
of the annotation that defines a SpEL
expression, which should match
to actually invoke the method for a particular event.
The following example shows how our notifier can be rewritten to be invoked only if the
content
attribute of the event is equal to my-event
:
-
Java
-
Kotlin
@EventListener(condition = "#blEvent.content == 'my-event'")
public void processBlockedListEvent(BlockedListEvent blEvent) {
// notify appropriate parties via notificationAddress...
}
@EventListener(condition = "#blEvent.content == 'my-event'")
fun processBlockedListEvent(blEvent: BlockedListEvent) {
// notify appropriate parties via notificationAddress...
}
Each SpEL
expression evaluates against a dedicated context. The following table lists the
items made available to the context so that you can use them for conditional event processing:
Name | Location | Description | Example |
---|---|---|---|
Event |
root object |
The actual |
|
Arguments array |
root object |
The arguments (as an object array) used to invoke the method. |
|
Argument name |
evaluation context |
The name of a particular method argument. If the names are not available
(for example, because the code was compiled without the |
|
Note that #root.event
gives you access to the underlying event, even if your method
signature actually refers to an arbitrary object that was published.
If you need to publish an event as the result of processing another event, you can change the method signature to return the event that should be published, as the following example shows:
-
Java
-
Kotlin
@EventListener
public ListUpdateEvent handleBlockedListEvent(BlockedListEvent event) {
// notify appropriate parties via notificationAddress and
// then publish a ListUpdateEvent...
}
@EventListener
fun handleBlockedListEvent(event: BlockedListEvent): ListUpdateEvent {
// notify appropriate parties via notificationAddress and
// then publish a ListUpdateEvent...
}
This feature is not supported for asynchronous listeners. |
The handleBlockedListEvent()
method publishes a new ListUpdateEvent
for every
BlockedListEvent
that it handles. If you need to publish several events, you can return
a Collection
or an array of events instead.
Asynchronous Listeners
If you want a particular listener to process events asynchronously, you can reuse the
regular @Async
support.
The following example shows how to do so:
-
Java
-
Kotlin
@EventListener
@Async
public void processBlockedListEvent(BlockedListEvent event) {
// BlockedListEvent is processed in a separate thread
}
@EventListener
@Async
fun processBlockedListEvent(event: BlockedListEvent) {
// BlockedListEvent is processed in a separate thread
}
Be aware of the following limitations when using asynchronous events:
-
If an asynchronous event listener throws an
Exception
, it is not propagated to the caller. SeeAsyncUncaughtExceptionHandler
for more details. -
Asynchronous event listener methods cannot publish a subsequent event by returning a value. If you need to publish another event as the result of the processing, inject an
ApplicationEventPublisher
to publish the event manually. -
ThreadLocals and logging context are not propagated by default for the event processing. See the
@EventListener
Observability section for more information on Observability concerns.
Ordering Listeners
If you need one listener to be invoked before another one, you can add the @Order
annotation to the method declaration, as the following example shows:
-
Java
-
Kotlin
@EventListener
@Order(42)
public void processBlockedListEvent(BlockedListEvent event) {
// notify appropriate parties via notificationAddress...
}
@EventListener
@Order(42)
fun processBlockedListEvent(event: BlockedListEvent) {
// notify appropriate parties via notificationAddress...
}
Generic Events
You can also use generics to further define the structure of your event. Consider using an
EntityCreatedEvent<T>
where T
is the type of the actual entity that got created. For example, you
can create the following listener definition to receive only EntityCreatedEvent
for a
Person
:
-
Java
-
Kotlin
@EventListener
public void onPersonCreated(EntityCreatedEvent<Person> event) {
// ...
}
@EventListener
fun onPersonCreated(event: EntityCreatedEvent<Person>) {
// ...
}
Due to type erasure, this works only if the event that is fired resolves the generic
parameters on which the event listener filters (that is, something like
class PersonCreatedEvent extends EntityCreatedEvent<Person> { … }
).
In certain circumstances, this may become quite tedious if all events follow the same
structure (as should be the case for the event in the preceding example). In such a case,
you can implement ResolvableTypeProvider
to guide the framework beyond what the runtime
environment provides. The following event shows how to do so:
-
Java
-
Kotlin
public class EntityCreatedEvent<T> extends ApplicationEvent implements ResolvableTypeProvider {
public EntityCreatedEvent(T entity) {
super(entity);
}
@Override
public ResolvableType getResolvableType() {
return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(getSource()));
}
}
class EntityCreatedEvent<T>(entity: T) : ApplicationEvent(entity), ResolvableTypeProvider {
override fun getResolvableType(): ResolvableType? {
return ResolvableType.forClassWithGenerics(javaClass, ResolvableType.forInstance(getSource()))
}
}
This works not only for ApplicationEvent but any arbitrary object that you send as
an event.
|
Finally, as with classic ApplicationListener
implementations, the actual multicasting
happens via a context-wide ApplicationEventMulticaster
at runtime. By default, this is a
SimpleApplicationEventMulticaster
with synchronous event publication in the caller thread.
This can be replaced/customized through an "applicationEventMulticaster" bean definition,
e.g. for processing all events asynchronously and/or for handling listener exceptions:
@Bean
ApplicationEventMulticaster applicationEventMulticaster() {
SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
multicaster.setTaskExecutor(...);
multicaster.setErrorHandler(...);
return multicaster;
}
Convenient Access to Low-level Resources
For optimal usage and understanding of application contexts, you should familiarize
yourself with Spring’s Resource
abstraction, as described in
Resources.
An application context is a ResourceLoader
, which can be used to load Resource
objects.
A Resource
is essentially a more feature rich version of the JDK java.net.URL
class.
In fact, implementations of 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 are injected into it like any other properties. You can specify those Resource
properties as simple String
paths and rely on automatic conversion from 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 according to the specific
context implementation. For example 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.
Application Startup Tracking
The ApplicationContext
manages the lifecycle of Spring applications and provides a rich
programming model around components. As a result, complex applications can have equally
complex component graphs and startup phases.
Tracking the application startup steps with specific metrics can help understand where time is being spent during the startup phase, but it can also be used as a way to better understand the context lifecycle as a whole.
The AbstractApplicationContext
(and its subclasses) is instrumented with an
ApplicationStartup
, which collects StartupStep
data about various startup phases:
-
application context lifecycle (base packages scanning, config classes management)
-
beans lifecycle (instantiation, smart initialization, post processing)
-
application events processing
Here is an example of instrumentation in the AnnotationConfigApplicationContext
:
-
Java
-
Kotlin
// create a startup step and start recording
StartupStep scanPackages = getApplicationStartup().start("spring.context.base-packages.scan");
// add tagging information to the current step
scanPackages.tag("packages", () -> Arrays.toString(basePackages));
// perform the actual phase we're instrumenting
this.scanner.scan(basePackages);
// end the current step
scanPackages.end();
// create a startup step and start recording
val scanPackages = getApplicationStartup().start("spring.context.base-packages.scan")
// add tagging information to the current step
scanPackages.tag("packages", () -> Arrays.toString(basePackages))
// perform the actual phase we're instrumenting
this.scanner.scan(basePackages)
// end the current step
scanPackages.end()
The application context is already instrumented with multiple steps. Once recorded, these startup steps can be collected, displayed and analyzed with specific tools. For a complete list of existing startup steps, you can check out the dedicated appendix section.
The default ApplicationStartup
implementation is a no-op variant, for minimal overhead.
This means no metrics will be collected during application startup by default.
Spring Framework ships with an implementation for tracking startup steps with Java Flight Recorder:
FlightRecorderApplicationStartup
. To use this variant, you must configure an instance of it
to the ApplicationContext
as soon as it’s been created.
Developers can also use the ApplicationStartup
infrastructure if they’re providing their own
AbstractApplicationContext
subclass, or if they wish to collect more precise data.
ApplicationStartup is meant to be only used during application startup and for
the core container; this is by no means a replacement for Java profilers or
metrics libraries like Micrometer.
|
To start collecting custom StartupStep
, components can either get the ApplicationStartup
instance from the application context directly, make their component implement ApplicationStartupAware
,
or ask for the ApplicationStartup
type on any injection point.
Developers should not use the "spring.*" namespace when creating custom startup steps.
This namespace is reserved for internal Spring usage and is subject to change.
|
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.
You can register an ApplicationContext
by using the ContextLoaderListener
, as the
following example shows:
<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>
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 are searched. Ant-style path patterns are supported as well.
Examples are /WEB-INF/*Context.xml
(for all files with names that end with
Context.xml
and that reside in the WEB-INF
directory) and /WEB-INF/**/*Context.xml
(for all such files in any subdirectory of WEB-INF
).
Deploying a Spring ApplicationContext
as a Jakarta EE RAR File
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 Jakarta EE RAR deployment
unit. This is the equivalent of bootstrapping a stand-alone ApplicationContext
(only hosted
in Jakarta EE environment) being able to access the Jakarta EE servers facilities. RAR deployment
is a more natural alternative to a 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 Jakarta EE 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
DataSource
instances and JMS ConnectionFactory
instances and can 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.
See the javadoc of the
SpringContextResourceAdapter
class for the configuration details involved in RAR deployment.
For a simple deployment of a Spring ApplicationContext as a Jakarta EE 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 the javadoc forSpringContextResourceAdapter
) and the corresponding Spring XML bean definition file(s) (typicallyMETA-INF/applicationContext.xml
). -
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
or react 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 may be used
by other application modules on the same machine.
|