|
对于最新的稳定版本,请使用 Spring Framework 7.0.6! |
Spring框架的其他功能
如章节介绍所述,org.springframework.beans.factory包提供了管理和操作bean的基础功能,包括以编程方式实现。除了扩展其他接口以更面向应用框架的风格提供额外功能外,org.springframework.context包还新增了ApplicationContext接口,该接口扩展了BeanFactory接口。许多用户以完全声明式方式使用ApplicationContext,甚至不通过编程创建它,而是依赖诸如ContextLoader等支持类,在作为Jakarta EE Web应用程序常规启动过程的一部分时自动实例化ApplicationContext。
为了以更面向框架的方式增强 BeanFactory 的功能,上下文
包还提供了以下功能:
-
通过
MessageSource接口以国际化风格访问消息。 -
通过
ResourceLoader接口访问资源,例如 URLs 和文件。 -
事件发布,即通过使用
ApplicationEventPublisher接口,将事件发布给实现ApplicationListener接口的 Bean。 -
通过
HierarchicalBeanFactory接口加载多个(分层)上下文,使每个上下文专注于应用程序的某一层,例如网络层。
使用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): 从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。任何用于解析消息的请求都会通过 ResourceBundle 对象按照 JDK 标准方式处理。为了示例的目的,假设上述两个资源绑定文件的内容如下:
# 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实现遵循与标准JDKResourceBundle相同的区域设置解析和回退规则。简而言之,继续使用之前定义的示例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 的引用。
任何在实现 MessageSourceAware 接口的 ApplicationContext 中定义的 bean 在创建和配置时都会被注入应用程序上下文的 MessageSource。
由于 Spring 的 MessageSource 基于 Java 的 ResourceBundle,因此不会合并具有相同基本名称的资源包,只会使用找到的第一个资源包。
后续具有相同基本名称的消息资源包将被忽略。 |
作为 ResourceBundleMessageSource 的替代方案,Spring 提供了一个
ReloadableResourceBundleMessageSource 类。此变体支持相同的资源包文件格式,但比基于标准 JDK 的
ResourceBundleMessageSource 实现更加灵活。特别是,它允许从任何 Spring 资源位置读取文件(而不仅仅是类路径),并且支持资源包属性文件的热重新加载(在两者之间高效缓存)。
请参阅 ReloadableResourceBundleMessageSource
javadoc 了解详细信息。 |
标准事件和自定义事件
通过 ApplicationContext 类和 ApplicationEvent 接口提供 ApplicationContext 中的事件处理功能。如果将实现了 ApplicationListener 接口的 Bean 部署到上下文中,每当有 ApplicationEvent 发布到 ApplicationContext 时,该 Bean 会被通知。这本质上是标准的观察者设计模式。
从 Spring 4.2 开始,事件基础设施得到了显著改进,并提供了
一个 基于注解的模型 以及
发布任何任意事件(即,一个不一定从 ApplicationEvent 继承的对象)的能力。当发布此类对象时,我们会为您将其包装成一个事件。 |
下表描述了Spring提供的标准事件:
| 事件 | 说明 |
|---|---|
|
在 |
|
在使用 |
|
在使用 |
|
在使用 |
|
一个与网络相关的事件,用于通知所有Bean HTTP请求已经得到处理。此事件在请求完成后发布。此事件仅适用于使用 Spring 的 |
|
一个添加了与 Servlet 特定的上下文信息的 |
您也可以创建并发布自己的自定义事件。下面的示例显示了一个继承 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,请调用 publishEvent() 方法 on 一个 ApplicationEventPublisher。通常,这是通过创建一个实现 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 定义。
以下示例显示了用于注册和配置上述每个类的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>
将所有内容综合起来,当调用sendEmail() bean的emailService方法时,如果有任何应该被阻止的电子邮件消息,会发布类型为BlockedListEvent的自定义事件。 blockedListNotifier bean被注册为ApplicationListener,并接收BlockedListEvent,此时它可以通知相关方。
| Spring 的事件机制旨在实现同一应用上下文中的 Spring beans 之间的简单通信。然而,对于更复杂的企业集成需求,单独维护的 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() 方法为它处理的每个 ListUpdateEvent 发布一个新的 BlockedListEvent。如果您需要发布多个事件,可以返回一个 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以手动发布事件。
排序监听器
如果您希望某个监听器在另一个监听器之前被调用,可以在方法声明上添加 @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,其中 T 是实际创建的实体的类型。例如,您可以创建以下监听器定义,以仅接收 EntityCreatedEvent 对于 Person:
-
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 上。
开发者也可以在他们提供自己的
ApplicationStartup 类型时,或者希望收集更精确数据时使用 AbstractApplicationContext 基础设施。
ApplicationStartup 仅应在应用程序启动期间和核心容器中使用;这绝不是 Java 分析工具或像 Micrometer 这样的度量库的替代品。 |
要开始收集自定义 StartupStep,组件可以直接从应用程序上下文中获取 ApplicationStartup
实例,使其组件实现 ApplicationStartupAware,
或者在任何注入点请求 ApplicationStartup 类型。
开发者在创建自定义启动步骤时不应使用 "spring.*" 命名空间。
此命名空间专用于内部 Spring 使用,并可能发生变化。 |
为Web应用程序提供便捷的ApplicationContext实例化
您可以使用例如ApplicationContext来声明式地创建ContextLoader实例。当然,您也可以通过使用其中一个ApplicationContext实现来编程方式地创建ApplicationContext实例。
您可以使用 ApplicationContext 来注册一个 ContextLoaderListener,如下例所示:
<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服务器设施。相较于部署无头WAR文件的方案——实际上是一个不包含任何HTTP入口点、仅用于在Jakarta EE环境中引导SpringApplicationContext的WAR文件——RAR部署是更为自然的选择。
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 文件:
-
Package 所有应用程序类打包到一个 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端点,这些端点可以被同一台机器上的其他应用程序模块使用。 |