ApplicationContext
的附加功能
正如在章节引言中所讨论的,org.springframework.beans.factory
包提供了管理和操作Bean的基本功能,包括以编程方式。org.springframework.context
包添加了ApplicationContext
接口,该接口扩展了BeanFactory
接口,并扩展了其他接口以以更面向应用程序框架的风格提供附加功能。许多人完全以声明式方式使用ApplicationContext
,甚至不以编程方式创建它,而是依赖于诸如ContextLoader
之类的支持类,在Jakarta EE Web应用程序的正常启动过程中自动实例化ApplicationContext
。
为了以更面向框架的风格增强BeanFactory
功能,context包还提供了以下功能:
-
通过
MessageSource
接口访问i18n风格的消息。 -
通过
ResourceLoader
接口访问资源,例如URL和文件。 -
事件发布,即通过使用
ApplicationEventPublisher
接口发布给实现ApplicationListener
接口的Bean。 -
加载多个(分层)上下文,让每个上下文都专注于一个特定的层,例如应用程序的Web层,通过
HierarchicalBeanFactory
接口。
使用MessageSource
进行国际化
ApplicationContext
接口扩展了一个名为MessageSource
的接口,因此提供了国际化(“i18n”)功能。Spring还提供了HierarchicalMessageSource
接口,它可以分层地解析消息。这两个接口共同构成了Spring实现消息解析的基础。这些接口上定义的方法包括:
-
String getMessage(String code, Object[] args, String default, Locale loc)
:用于从MessageSource
检索消息的基本方法。当找不到指定区域设置的消息时,将使用默认消息。传入的任何参数都将成为替换值,使用标准库提供的MessageFormat
功能。 -
String getMessage(String code, Object[] args, Locale loc)
:与前面方法基本相同,但有一点不同:无法指定默认消息。如果找不到消息,则会抛出NoSuchMessageException
。 -
String getMessage(MessageSourceResolvable resolvable, Locale locale)
:前面方法中使用的所有属性也封装在一个名为MessageSourceResolvable
的类中,您可以使用此方法。
加载ApplicationContext
时,它会自动搜索在上下文中定义的MessageSource
bean。该bean必须具有名称messageSource
。如果找到这样的bean,则所有对前面方法的调用都将委托给消息源。如果没有找到消息源,ApplicationContext
将尝试查找包含具有相同名称的bean的父级。如果找到,它将使用该bean作为MessageSource
。如果ApplicationContext
找不到任何消息源,则会实例化一个空的DelegatingMessageSource
以便能够接受对上述方法的调用。
Spring提供了三个MessageSource
实现:ResourceBundleMessageSource
、ReloadableResourceBundleMessageSource
和StaticMessageSource
。它们都实现了HierarchicalMessageSource
以便进行嵌套消息处理。StaticMessageSource
很少使用,但提供了以编程方式向源添加消息的方法。以下示例显示了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>
此示例假设您在类路径中定义了三个名为format
、exceptions
和windows
的资源包。任何解析消息的请求都将以JDK标准方式通过ResourceBundle
对象解析消息。出于示例的目的,假设上述两个资源包文件的内容如下所示:
# in format.properties
message=Alligators rock!
# in exceptions.properties
argument.required=The {0} argument is required.
下一个示例显示了一个运行MessageSource
功能的程序。请记住,所有ApplicationContext
实现也是MessageSource
实现,因此可以转换为MessageSource
接口。
-
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)
}
上述程序的输出结果如下所示:
Alligators rock!
总而言之,MessageSource
定义在一个名为beans.xml
的文件中,该文件位于类路径的根目录下。messageSource
bean定义通过其basenames
属性引用多个资源包。传递给basenames
属性列表中的三个文件作为文件存在于类路径的根目录下,分别称为format.properties
、exceptions.properties
和windows.properties
。
接下来的示例展示了传递给消息查找的参数。这些参数被转换为String
对象,并插入到查找消息中的占位符中。
<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)
}
}
调用execute()
方法后的输出结果如下所示
The userDao argument is required.
关于国际化(“i18n”),Spring 的各种MessageSource
实现遵循与标准 JDK ResourceBundle
相同的区域设置解析和回退规则。简而言之,继续使用前面定义的示例messageSource
,如果您想针对英国(en-GB
)区域设置解析消息,则应分别创建名为format_en_GB.properties
、exceptions_en_GB.properties
和windows_en_GB.properties
的文件。
通常,区域设置解析由应用程序的周围环境管理。在下面的示例中,手动指定了用于解析(英国)消息的区域设置
# 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)
}
上述程序运行后的输出结果如下所示
Ebagum lad, the 'userDao' argument is required, I say, required.
您还可以使用MessageSourceAware
接口获取对任何已定义MessageSource
的引用。在ApplicationContext
中定义的任何实现MessageSourceAware
接口的bean,在bean创建和配置时,都会注入应用程序上下文MessageSource
。
因为Spring的MessageSource 基于Java的ResourceBundle ,所以它不会合并具有相同基名的bundle,而只会使用找到的第一个bundle。后续具有相同基名的消息bundle将被忽略。 |
作为ResourceBundleMessageSource 的替代方案,Spring 提供了一个ReloadableResourceBundleMessageSource 类。此变体支持相同的bundle文件格式,但比基于标准JDK的ResourceBundleMessageSource 实现更灵活。特别是,它允许从任何Spring资源位置(不仅仅是从类路径)读取文件,并支持bundle属性文件的热重载(同时在两者之间有效地缓存它们)。有关详细信息,请参阅ReloadableResourceBundleMessageSource javadoc。 |
标准和自定义事件
ApplicationContext
中的事件处理通过ApplicationEvent
类和ApplicationListener
接口提供。如果将实现ApplicationListener
接口的bean部署到上下文中,则每次将ApplicationEvent
发布到ApplicationContext
时,都会通知该bean。从本质上讲,这就是标准的观察者设计模式。
从Spring 4.2开始,事件基础结构得到了显著改进,并提供了一个基于注解的模型,以及发布任何任意事件(即,不一定扩展自ApplicationEvent 的对象)的能力。发布此类对象时,我们会将其包装在一个事件中。 |
下表描述了Spring提供的标准事件
事件 | 说明 |
---|---|
|
当 |
|
当使用 |
|
当使用 |
|
当使用 |
|
一个特定于Web的事件,告诉所有bean已处理HTTP请求。此事件在请求完成后发布。此事件仅适用于使用Spring的 |
|
|
您还可以创建和发布您自己的自定义事件。以下示例显示了一个扩展Spring的ApplicationEvent
基类的简单类
-
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)
要发布自定义ApplicationEvent
,请在ApplicationEventPublisher
上调用publishEvent()
方法。通常,这是通过创建一个实现ApplicationEventPublisherAware
的类并将其注册为Spring bean来完成的。以下示例显示了这样一个类
-
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...
}
}
在配置时,Spring容器检测到EmailService
实现了ApplicationEventPublisherAware
,并自动调用setApplicationEventPublisher()
。实际上,传入的参数是Spring容器本身。您正在通过其ApplicationEventPublisher
接口与应用程序上下文进行交互。
要接收自定义ApplicationEvent
,您可以创建一个实现ApplicationListener
的类并将其注册为Spring bean。以下示例显示了这样一个类
-
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...
}
}
请注意,ApplicationListener
使用自定义事件的类型(在前面的示例中为BlockedListEvent
)进行泛型参数化。这意味着onApplicationEvent()
方法可以保持类型安全,无需任何向下转换。您可以注册任意数量的事件侦听器,但请注意,默认情况下,事件侦听器会同步接收事件。这意味着publishEvent()
方法会阻塞,直到所有侦听器都完成处理事件为止。这种同步和单线程方法的一个优点是,当侦听器接收事件时,如果可用事务上下文,它将在发布者的事务上下文中操作。如果需要另一种事件发布策略,例如默认情况下异步事件处理,请参阅Spring的ApplicationEventMulticaster
接口和SimpleApplicationEventMulticaster
实现的javadoc,了解可以应用于自定义“applicationEventMulticaster”bean定义的配置选项。在这些情况下,不会为事件处理传播ThreadLocals和日志记录上下文。有关可观察性问题的更多信息,请参阅@EventListener
可观察性部分。
以下示例显示了用于注册和配置上述每个类的bean定义
<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>
总而言之,当调用emailService
bean的sendEmail()
方法时,如果存在任何应阻止的电子邮件消息,则会发布类型为BlockedListEvent
的自定义事件。blockedListNotifier
bean注册为ApplicationListener
并接收BlockedListEvent
,此时它可以通知相关方。
Spring的事件机制旨在用于同一应用程序上下文内Spring bean之间的简单通信。但是,对于更复杂的企业集成需求,单独维护的Spring Integration项目提供了对构建轻量级、面向模式的、事件驱动的架构的完整支持,这些架构建立在众所周知的Spring编程模型之上。 |
基于注解的事件侦听器
您可以使用@EventListener
注解在托管bean的任何方法上注册事件侦听器。BlockedListNotifier
可以改写如下
-
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...
}
}
方法签名再次声明它侦听的事件类型,但这次使用了灵活的名称,并且没有实现特定的侦听器接口。只要实际事件类型在其实现层次结构中解析您的泛型参数,也可以通过泛型缩小事件类型。
如果您的方法应该侦听多个事件,或者您希望将其定义为根本没有参数,则也可以在注解本身中指定事件类型。以下示例显示了如何操作
-
Java
-
Kotlin
@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
// ...
}
@EventListener(ContextStartedEvent::class, ContextRefreshedEvent::class)
fun handleContextStart() {
// ...
}
还可以通过使用定义SpEL
表达式的注解的condition
属性添加其他运行时过滤,该表达式应该匹配才能实际为特定事件调用该方法。
以下示例显示了如何改写我们的通知程序,使其仅在事件的content
属性等于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...
}
每个SpEL
表达式都会针对专用上下文进行评估。下表列出了提供给上下文的项目,以便您可以将它们用于条件事件处理
名称 | 位置 | 描述 | 示例 |
---|---|---|---|
事件 |
根对象 |
实际的 |
|
参数数组 |
根对象 |
用于调用该方法的参数(作为对象数组)。 |
|
参数名称 |
评估上下文 |
特定方法参数的名称。如果名称不可用(例如,因为代码是在没有 |
|
请注意,即使您的方法签名实际引用的是已发布的任意对象,#root.event
也可以让您访问底层事件。
如果您需要发布事件作为处理另一个事件的结果,您可以将方法签名更改为返回应发布的事件,如下例所示
-
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...
}
此功能不支持异步侦听器。 |
handleBlockedListEvent()
方法为其处理的每个 BlockedListEvent
事件发布一个新的 ListUpdateEvent
事件。如果您需要发布多个事件,可以返回一个 Collection
或事件数组。
异步监听器
如果您希望某个监听器异步处理事件,您可以重用 常规的 @Async
支持。以下示例演示了如何操作
-
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
}
使用异步事件时,请注意以下限制
-
如果异步事件监听器抛出
Exception
,则不会将其传播给调用方。有关详细信息,请参阅AsyncUncaughtExceptionHandler
。 -
异步事件监听器方法不能通过返回值来发布后续事件。如果您需要发布另一个事件作为处理结果,请注入
ApplicationEventPublisher
以手动发布事件。 -
默认情况下,不会传播 ThreadLocals 和日志记录上下文以进行事件处理。有关可观察性问题的更多信息,请参阅
@EventListener
可观察性部分。
监听器排序
如果您需要一个监听器在另一个监听器之前被调用,您可以向方法声明添加 @Order
注解,如下例所示
-
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...
}
泛型事件
您还可以使用泛型来进一步定义事件的结构。考虑使用 EntityCreatedEvent<T>
,其中 T
是实际创建的实体的类型。例如,您可以创建以下监听器定义,以仅接收 Person
的 EntityCreatedEvent
-
Java
-
Kotlin
@EventListener
public void onPersonCreated(EntityCreatedEvent<Person> event) {
// ...
}
@EventListener
fun onPersonCreated(event: EntityCreatedEvent<Person>) {
// ...
}
由于类型擦除,只有在触发的事件解析了事件监听器筛选的泛型参数(例如,类似于 class PersonCreatedEvent extends EntityCreatedEvent<Person> { … }
)时,此方法才有效。
在某些情况下,如果所有事件都遵循相同的结构(如前面的示例中的事件应该那样),这可能会变得相当繁琐。在这种情况下,您可以实现 ResolvableTypeProvider
来引导框架超越运行时环境提供的内容。以下事件演示了如何操作
-
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()))
}
}
这不仅适用于 ApplicationEvent ,也适用于您作为事件发送的任何任意对象。 |
最后,与经典的 ApplicationListener
实现一样,实际的多播是在运行时通过上下文范围的 ApplicationEventMulticaster
进行的。默认情况下,这是一个在调用线程中进行同步事件发布的 SimpleApplicationEventMulticaster
。例如,为了异步处理所有事件和/或处理监听器异常,可以通过“applicationEventMulticaster”bean 定义来替换/自定义它。
@Bean
ApplicationEventMulticaster applicationEventMulticaster() {
SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
multicaster.setTaskExecutor(...);
multicaster.setErrorHandler(...);
return multicaster;
}
方便地访问底层资源
为了优化使用和理解应用程序上下文,您应该熟悉 Spring 的 Resource
抽象,如 资源 中所述。
应用程序上下文是一个 ResourceLoader
,可用于加载 Resource
对象。Resource
本质上是 JDK java.net.URL
类的一个功能更丰富的版本。实际上,Resource
的实现会在适当的情况下包装 java.net.URL
的实例。Resource
可以以透明的方式从几乎任何位置获取底层资源,包括类路径、文件系统位置、任何可以用标准 URL 描述的位置以及其他一些变体。如果资源位置字符串是一个简单的路径,没有任何特殊的前缀,那么这些资源的来源是特定于实际应用程序上下文类型的。
您可以配置部署到应用程序上下文中的 bean 以实现特殊的回调接口 ResourceLoaderAware
,以便在初始化时自动回调,并将应用程序上下文本身作为 ResourceLoader
传递进去。您还可以公开 Resource
类型的属性,用于访问静态资源。它们像任何其他属性一样被注入到其中。您可以将这些 Resource
属性指定为简单的 String
路径,并在部署 bean 时依赖于将这些文本字符串自动转换为实际 Resource
对象。
提供给 ApplicationContext
构造函数的位置路径实际上是资源字符串,并且在简单形式下,会根据具体的上下文实现进行适当的处理。例如,ClassPathXmlApplicationContext
将简单的路径视为类路径位置。您还可以使用带有特殊前缀的位置路径(资源字符串)来强制加载来自类路径或 URL 的定义,而不管实际的上下文类型如何。
应用程序启动跟踪
ApplicationContext
管理 Spring 应用程序的生命周期,并提供围绕组件的丰富的编程模型。因此,复杂的应用程序可能具有同样复杂的组件图和启动阶段。
使用特定指标跟踪应用程序启动步骤可以帮助理解在启动阶段花费的时间,但它也可以作为更好地理解上下文生命周期整体的一种方式。
AbstractApplicationContext
(及其子类)使用 ApplicationStartup
进行检测,该 ApplicationStartup
收集关于各种启动阶段的 StartupStep
数据
-
应用程序上下文生命周期(基本包扫描、配置类管理)
-
bean 生命周期(实例化、智能初始化、后处理)
-
应用程序事件处理
这是 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()
应用程序上下文已经使用多个步骤进行了检测。记录后,可以使用特定工具收集、显示和分析这些启动步骤。有关现有启动步骤的完整列表,您可以查看 专用附录部分。
默认的 ApplicationStartup
实现是一个无操作变体,以实现最小的开销。这意味着默认情况下,在应用程序启动期间不会收集任何指标。Spring Framework 提供了一个用于使用 Java Flight Recorder 跟踪启动步骤的实现:FlightRecorderApplicationStartup
。要使用此变体,您必须在创建 ApplicationContext
后立即将其实例配置到 ApplicationContext
中。
如果开发人员提供他们自己的 AbstractApplicationContext
子类,或者他们希望收集更精确的数据,他们也可以使用 ApplicationStartup
基础设施。
ApplicationStartup 只应在应用程序启动期间和核心容器中使用;这绝不是 Java 分析器或 Micrometer 等指标库的替代品。Micrometer |
要开始收集自定义 StartupStep
,组件可以从应用程序上下文直接获取 ApplicationStartup
实例,使他们的组件实现 ApplicationStartupAware
,或者在任何注入点请求 ApplicationStartup
类型。
开发人员在创建自定义启动步骤时不应使用 "spring.*" 命名空间。此命名空间保留用于 Spring 内部使用,可能会发生更改。 |
Web 应用程序的便捷 ApplicationContext 实例化
您可以使用例如 ContextLoader
声明式地创建 ApplicationContext
实例。当然,您也可以使用 ApplicationContext
实现之一以编程方式创建 ApplicationContext
实例。
您可以使用 ContextLoaderListener
注册 ApplicationContext
,如下例所示
<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>
侦听器检查 contextConfigLocation
参数。如果参数不存在,侦听器将使用 /WEB-INF/applicationContext.xml
作为默认值。当参数存在时,侦听器使用预定义的分隔符(逗号、分号和空格)分隔 String
,并将这些值用作搜索应用程序上下文的位置。也支持 Ant 风格的路径模式。例如 /WEB-INF/*Context.xml
(对于所有名称以 Context.xml
结尾且位于 WEB-INF
目录中的文件)和 /WEB-INF/**/*Context.xml
(对于 WEB-INF
的任何子目录中的所有此类文件)。
将 Spring ApplicationContext
部署为 Jakarta EE RAR 文件
可以将 Spring ApplicationContext
部署为 RAR 文件,将上下文及其所有必需的 bean 类和库 JAR 封装在 Jakarta EE RAR 部署单元中。这相当于引导一个独立的 ApplicationContext
(仅托管在 Jakarta EE 环境中),能够访问 Jakarta EE 服务器的设施。RAR 部署是部署无头 WAR 文件(实际上是一个没有任何 HTTP 入口点的 WAR 文件,仅用于在 Jakarta EE 环境中引导 Spring ApplicationContext
)场景的更自然的替代方案。
RAR 部署非常适合不需要 HTTP 入口点,而是仅由消息端点和计划作业组成的应用程序上下文。此类上下文中的 bean 可以使用应用程序服务器资源,例如 JTA 事务管理器和 JNDI 绑定的 JDBC DataSource
实例和 JMS ConnectionFactory
实例,还可以向平台的 JMX 服务器注册——所有这些都通过 Spring 的标准事务管理和 JNDI 和 JMX 支持设施。应用程序组件还可以通过 Spring 的 TaskExecutor
抽象与应用程序服务器的 JCA WorkManager
交互。
有关 RAR 部署中涉及的配置详细信息,请参阅 SpringContextResourceAdapter
类的 javadoc。
将 Spring ApplicationContext 作为 Jakarta EE RAR 文件进行简单部署
-
将所有应用程序类打包到 RAR 文件中(这是一个带有不同文件扩展名的标准 JAR 文件)。
-
将所有必需的库 JAR 添加到 RAR 存档的根目录。
-
添加
META-INF/ra.xml
部署描述符(如SpringContextResourceAdapter
的 javadoc 中所示)和相应的 Spring XML bean 定义文件(通常为META-INF/applicationContext.xml
)。 -
将生成的 RAR 文件放入应用程序服务器的部署目录。
此类 RAR 部署单元通常是自包含的。它们不会将组件公开给外部世界,甚至不会公开给同一应用程序的其他模块。与基于 RAR 的 ApplicationContext 的交互通常通过它与其他模块共享的 JMS 目标进行。基于 RAR 的 ApplicationContext 也可能例如安排一些作业或对文件系统(或类似内容)中的新文件做出反应。如果它需要允许来自外部的同步访问,它可以(例如)导出 RMI 端点,其他应用程序模块可以在同一台机器上使用这些端点。 |