正如本章介绍中所讨论的,该软件包提供了管理和操作 Bean 的基本功能,包括 程序化方式。该包添加了 ApplicationContext 接口,该接口除了扩展其他接口外,还扩展了接口。 接口,可在更多应用程序中提供附加功能 面向框架的风格。许多人完全使用 声明式时尚,甚至不是以编程方式创建它,而是依赖于 支持类,例如在 Jakarta EE Web 应用程序的正常启动过程中自动实例化。org.springframework.beans.factoryorg.springframework.contextBeanFactoryApplicationContextContextLoaderApplicationContext

为了以更面向框架的风格增强功能,上下文 软件包还提供以下功能:BeanFactory

  • 通过界面访问 i18n 样式的消息。MessageSource

  • 通过界面访问资源,例如 URL 和文件。ResourceLoader

  • 事件发布,即对实现接口的 Bean, 通过使用界面。ApplicationListenerApplicationEventPublisher

  • 加载多个(分层)上下文,让每个上下文都集中在一个上下文上 特定层,例如应用程序的 Web 层,通过接口。HierarchicalBeanFactory

国际化使用MessageSource

该接口扩展了一个名为 and 的接口, 因此,提供国际化(“I18N”)功能。Spring 还提供了接口,可以分层解析消息。 这些接口共同提供了 Spring 效果消息的基础 分辨率。在这些接口上定义的方法包括:ApplicationContextMessageSourceHierarchicalMessageSource

  • String getMessage(String code, Object[] args, String default, Locale loc):基本款 用于从 中检索消息的方法。当找不到任何消息时 对于指定的区域设置,使用默认消息。传入的任何参数都将成为 替换值,使用标准提供的功能 图书馆。MessageSourceMessageFormat

  • String getMessage(String code, Object[] args, Locale loc):基本相同 与前一种方法不同,但有一个区别:无法指定默认消息。如果 找不到消息,抛出 A。NoSuchMessageException

  • String getMessage(MessageSourceResolvable resolvable, Locale locale): 所有属性 在前面的方法中使用的也包装在名为 的类中,可以与此方法一起使用。MessageSourceResolvable

当加载 an 时,它会自动搜索上下文中定义的 bean。Bean 必须具有名称 。如果这样的豆子 找到,则对上述方法的所有调用都将委托给消息源。如果没有 找到消息源,尝试查找包含 同名的 Bean。如果是这样,它将使用该 Bean 作为 .如果找不到任何消息源,则实例化一个空,以便能够接受对 上面定义的方法。ApplicationContextMessageSourcemessageSourceApplicationContextMessageSourceApplicationContextDelegatingMessageSource

Spring 提供了三种实现,分别是 和 。它们都是为了做嵌套而实现的 消息。很少使用,但提供了编程方式 将消息添加到源。以下示例显示:MessageSourceResourceBundleMessageSourceReloadableResourceBundleMessageSourceStaticMessageSourceHierarchicalMessageSourceStaticMessageSourceResourceBundleMessageSource

<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>

该示例假定您有三个名为 的资源包,并在类路径中定义。任何解析消息的请求都是 以 JDK 标准的方式处理通过对象解析消息。对于 在本示例中,假定上述两个资源包文件的内容 具体如下:formatexceptionswindowsResourceBundle

# in format.properties
message=Alligators rock!
# in exceptions.properties
argument.required=The {0} argument is required.

下一个示例显示了用于运行该功能的程序。 请记住,所有实现也是实现,因此可以强制转换为接口。MessageSourceApplicationContextMessageSourceMessageSource

  • 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!

总而言之,在一个名为 的文件中定义了 ,该文件 存在于类路径的根目录中。bean 定义是指 通过其属性的资源包数。这三个文件是 在列表中传递给属性的文件作为文件存在于 classpath 和 分别称为 、 和 。MessageSourcebeans.xmlmessageSourcebasenamesbasenamesformat.propertiesexceptions.propertieswindows.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 的各种实现遵循与标准 JDK 相同的语言环境解析和回退规则。简而言之,并继续定义的示例 以前,如果要解析针对英国 () 区域设置的消息,则 将分别创建名为 、 和 的文件。MessageSourceResourceBundlemessageSourceen-GBformat_en_GB.propertiesexceptions_en_GB.propertieswindows_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.

您还可以使用该接口获取对任何已定义的引用。在实现接口的 an 中定义的任何 bean 都会注入 创建和配置 Bean 时的应用程序上下文。MessageSourceAwareMessageSourceApplicationContextMessageSourceAwareMessageSource

因为 Spring 是基于 Java 的,所以它不会合并 具有相同基本名称的捆绑包,但仅使用找到的第一个捆绑包。 具有相同基本名称的后续消息包将被忽略。MessageSourceResourceBundle
作为 的替代方法,Spring 提供了一个类。此变体支持相同的捆绑包 文件格式,但比基于 JDK 的标准实现更灵活。特别是,它允许阅读 来自任何 Spring 资源位置的文件(不仅来自类路径),并支持热 重新加载捆绑属性文件(同时在两者之间有效地缓存它们)。 有关详细信息,请参阅 ReloadableResourceBundleMessageSource javadoc。ResourceBundleMessageSourceReloadableResourceBundleMessageSourceResourceBundleMessageSource

标准事件和自定义事件

中的事件处理是通过类和接口提供的。如果实现接口的 Bean 部署到上下文中,则每次将 an 发布到 时,都会通知该 bean。 从本质上讲,这是标准的 Observer 设计模式。ApplicationContextApplicationEventApplicationListenerApplicationListenerApplicationEventApplicationContext

从春季 4.2 开始,活动基础设施得到了显着改进,并提供 基于注释的模型以及 能够发布任何任意事件(即,不一定 从 扩展 )。当这样的对象被发布时,我们将其包装在一个 活动。ApplicationEvent

下表描述了 Spring 提供的标准事件:

表 1.内置事件
事件 解释

ContextRefreshedEvent

在初始化或刷新时发布(例如,由 使用接口上的方法)。 在这里,“初始化”意味着加载所有 Bean,检测后处理器 Bean 并激活,单例被预先实例化,并且对象是 准备使用。只要上下文尚未关闭,就可以触发刷新 多次,前提是所选对象实际上支持此类 “热”刷新。例如,支持热刷新,但不支持。ApplicationContextrefresh()ConfigurableApplicationContextApplicationContextApplicationContextXmlWebApplicationContextGenericApplicationContext

ContextStartedEvent

在使用接口上的方法启动时发布。在这里,“started”表示所有 Bean 都接收到显式的启动信号。通常,此信号用于重新启动 Bean 在显式停止之后,但它也可用于启动尚未停止的组件 配置为自动启动(例如,尚未启动的组件) 初始化)。ApplicationContextstart()ConfigurableApplicationContextLifecycle

ContextStoppedEvent

在使用接口上的方法停止时发布。在这里,“停止”意味着所有 Bean 都接收到明确的停止信号。停止的上下文可以通过调用重新启动。ApplicationContextstop()ConfigurableApplicationContextLifecyclestart()

ContextClosedEvent

使用以下方法关闭时发布 在接口上或通过 JVM 关闭钩子。这里 “关闭”意味着所有单例 Bean 都将被销毁。关闭上下文后, 它已达到使用寿命,无法刷新或重新启动。ApplicationContextclose()ConfigurableApplicationContext

RequestHandledEvent

特定于 Web 的事件,告知所有 Bean 已处理 HTTP 请求。这 事件在请求完成后发布。此活动只适用于 使用 Spring 的 Web 应用程序。DispatcherServlet

ServletRequestHandledEvent

其子类添加特定于 Servlet 的上下文信息。RequestHandledEvent

您还可以创建和发布自己的自定义事件。以下示例显示了 扩展 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)

若要发布自定义项,请在 .通常,这是通过创建一个实现并将其注册为 Spring Bean 的类来完成的。以下 示例显示了这样的类:ApplicationEventpublishEvent()ApplicationEventPublisherApplicationEventPublisherAware

  • 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 容器会检测到实现并自动调用 .实际上,传入的参数是弹簧 容器本身。您正在通过应用程序上下文的界面与应用程序上下文进行交互。EmailServiceApplicationEventPublisherAwaresetApplicationEventPublisher()ApplicationEventPublisher

要接收自定义 ,您可以创建一个实现的类并将其注册为 Spring Bean。以下示例 显示这样的类:ApplicationEventApplicationListener

  • 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...
	}
}

请注意,它通常使用自定义事件的类型进行参数化(在前面的示例中)。 这意味着该方法可以保持类型安全,避免任何向下转换的需要。 您可以根据需要注册任意数量的事件侦听器,但请注意,默认情况下,事件侦听器会同步接收事件。 这意味着该方法将阻塞,直到所有侦听器都完成对事件的处理。 这种同步和单线程方法的一个优点是,当侦听器接收到事件时, 如果事务上下文可用,它将在发布者的事务上下文中运行。 如果需要其他事件发布策略,例如默认的异步事件处理, 请参阅 Spring 的 ApplicationEventMulticaster 接口的 javadoc 和 SimpleApplicationEventMulticaster 实现 用于可应用于自定义“applicationEventMulticaster”Bean 定义的配置选项。 在这些情况下,不会为事件处理传播 ThreadLocals 和日志记录上下文。 有关可观测性问题的更多信息,请参阅@EventListener可观测性部分ApplicationListenerBlockedListEventonApplicationEvent()publishEvent()

以下示例显示了用于注册和配置每个 以上课程:

<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>

把它们放在一起,当豆子的方法 调用时,如果存在任何应阻止的电子邮件,则会发布该类型的自定义事件。Bean 被注册为 并接收 ,此时它可以 通知有关各方。sendEmail()emailServiceBlockedListEventblockedListNotifierApplicationListenerBlockedListEvent

Spring 的事件机制专为 Spring Bean 之间的简单通信而设计 在同一应用程序上下文中。但是,对于更复杂的企业 集成需求,单独维护的 Spring Integration 项目提供 完全支持构建轻量级、面向模式、事件驱动的 建立在著名的 Spring 编程模型之上的体系结构。

基于注释的事件侦听器

您可以使用注释在托管 Bean 的任何方法上注册事件侦听器。可以按如下方式重写:@EventListenerBlockedListNotifier

  • 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...
	}
}

方法签名再次声明它侦听的事件类型, 但是,这一次,使用灵活的名称,并且没有实现特定的侦听器接口。 事件类型也可以通过泛型缩小范围,只要实际事件类型 解析其实现层次结构中的泛型参数。

如果你的方法应该侦听多个事件,或者如果你想用 no 来定义它 参数,也可以在注解本身上指定事件类型。这 以下示例演示如何执行此操作:

  • Java

  • Kotlin

@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
	// ...
}
@EventListener(ContextStartedEvent::class, ContextRefreshedEvent::class)
fun handleContextStart() {
	// ...
}

还可以使用属性添加其他运行时筛选 定义 SpEL 表达式的注释,该表达式应匹配 以实际调用特定事件的方法。condition

以下示例显示了如何重写我们的通知程序,使其仅在事件的属性等于 时才调用:contentmy-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

表 2.事件 SpEL 可用元数据
名字 位置 描述

事件

root 对象

实际的 .ApplicationEvent

#root.eventevent

参数数组

root 对象

用于调用方法的参数(作为对象数组)。

#root.args或; 访问第一个参数等。argsargs[0]

参数名称

评估上下文

任何方法参数的名称。如果由于某种原因,名称不可用 (例如,因为编译后的字节码中没有调试信息)、单个 参数也可以使用语法,其中代表 参数索引(从 0 开始)。#a<#arg><#arg>

#blEvent或者(您也可以使用 or 参数表示法作为别名)#a0#p0#p<#arg>

请注意,即使方法允许你访问基础事件,也是如此 签名实际上是指已发布的任意对象。#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()ListUpdateEventBlockedListEventCollection

异步侦听器

如果希望特定侦听器异步处理事件,可以重用常规@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
}

使用异步事件时,请注意以下限制:

对侦听器进行排序

如果需要先调用一个侦听器,可以将批注添加到方法声明中,如以下示例所示:@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...
}

通用事件

您还可以使用泛型来进一步定义事件的结构。请考虑使用 where 是已创建的实际实体的类型。例如,你 可以创建以下侦听器定义以仅接收:EntityCreatedEvent<T>TEntityCreatedEventPerson

  • 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

最后,与经典实现一样,实际的组播 在运行时通过上下文范围发生。默认情况下,这是调用方线程中的同步事件发布。 这可以通过“applicationEventMulticaster”bean 定义进行替换/定制, 例如,用于异步处理所有事件和/或处理侦听器异常:ApplicationListenerApplicationEventMulticasterSimpleApplicationEventMulticaster

@Bean
ApplicationEventMulticaster applicationEventMulticaster() {
	SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
	multicaster.setTaskExecutor(...);
	multicaster.setErrorHandler(...);
	return multicaster;
}

方便访问低级资源

为了获得最佳使用和对应用程序上下文的理解,您应该熟悉 你自己使用 Spring 的抽象,如 参考资料 中所述。Resource

应用程序上下文是 ,可用于加载对象。 A 本质上是 JDK 类的功能更丰富的版本。 实际上,包装实例的实现 ,其中 适当。A 可以从 A 中的几乎任何位置获取低级资源 透明的时尚,包括来自类路径、文件系统位置、任何地方 可以使用标准 URL 和其他一些变体进行描述。如果资源位置 string 是一条没有任何特殊前缀的简单路径,这些资源的来源是 特定且适合实际应用程序上下文类型。ResourceLoaderResourceResourcejava.net.URLResourcejava.net.URLResource

您可以配置部署到应用程序上下文中的 Bean 以实现特殊的 回调接口 ,将在 初始化时间,应用程序上下文本身作为 . 还可以公开 类型的属性,用于访问静态资源。 它们像任何其他属性一样被注入其中。您可以将这些属性指定为简单路径,并依赖于从这些文本进行自动转换 部署 Bean 时将字符串添加到实际对象。ResourceLoaderAwareResourceLoaderResourceResourceStringResource

提供给构造函数的一个或多个位置路径实际上是 资源字符串,并以简单的形式,根据特定的 上下文实现。例如,对待一个简单的 location path 作为类路径位置。还可以使用位置路径(资源字符串) 使用特殊前缀强制从类路径或 URL 加载定义, 与实际上下文类型无关。ApplicationContextClassPathXmlApplicationContext

应用程序启动跟踪

管理 Spring 应用程序的生命周期,并提供丰富的 围绕组件的编程模型。因此,复杂的应用程序可以同样具有 复杂的组件图和启动阶段。ApplicationContext

使用特定指标跟踪应用程序启动步骤有助于了解位置 在启动阶段花费了时间,但它也可以用作更好的一种方式 从整体上了解上下文生命周期。

(及其子类)使用 ,用于收集有关各个启动阶段的数据:AbstractApplicationContextApplicationStartupStartupStep

  • 应用程序上下文生命周期(基础包扫描、配置类管理)

  • 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()

应用程序上下文已使用多个步骤进行检测。 一旦记录下来,就可以使用特定工具收集、显示和分析这些启动步骤。 有关现有启动步骤的完整列表,您可以查看专用的附录部分

默认实现是无操作变体,以最小化开销。 这意味着默认情况下,在应用程序启动期间不会收集任何指标。 Spring Framework 附带了一个用于跟踪 Java Flight Recorder 启动步骤的实现:.要使用此变体,您必须配置它的实例 一旦它被创建。ApplicationStartupFlightRecorderApplicationStartupApplicationContext

如果开发人员提供自己的子类,或者希望收集更精确的数据,也可以使用基础结构。ApplicationStartupAbstractApplicationContext

ApplicationStartup仅用于应用程序启动期间,用于 核心容器;这绝不是 Java 分析器或 指标库,如 Micrometer

要开始收集自定义,组件可以直接从应用上下文中获取实例,让自己的组件实现, 或询问任何注射点的类型。StartupStepApplicationStartupApplicationStartupAwareApplicationStartup

开发人员在创建自定义启动步骤时不应使用命名空间。 此命名空间保留供 Spring 内部使用,并且可能会更改。"spring.*"

方便的 ApplicationWeb 应用程序的上下文实例化

例如,您可以使用 .当然,您也可以创建实例 通过使用其中一个实现以编程方式。ApplicationContextContextLoaderApplicationContextApplicationContext

您可以使用 注册一个 ,因为 以下示例显示:ApplicationContextContextLoaderListener

<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>

侦听器检查参数。如果参数没有 存在,侦听器用作默认值。当 参数确实存在,侦听器使用预定义的参数来分隔 分隔符(逗号、分号和空格),并使用这些值作为位置,其中 搜索应用程序上下文。还支持 Ant 样式的路径模式。 例如(对于名称以 结尾且驻留在目录中的所有文件)和 (对于 的任何子目录中的所有此类文件)。contextConfigLocation/WEB-INF/applicationContext.xmlString/WEB-INF/*Context.xmlContext.xmlWEB-INF/WEB-INF/**/*Context.xmlWEB-INF

将 Spring 部署为 Jakarta EE RAR 文件ApplicationContext

可以将 Spring 部署为 RAR 文件,封装 上下文及其所有必需的 Bean 类和库 JAR 在 Jakarta EE RAR 部署中 单位。这相当于引导一个独立的(仅托管的 在 Jakarta EE 环境中)能够访问 Jakarta EE 服务器设施。RAR 部署 是部署无头 WAR 文件方案的更自然的替代方法——实际上, 没有任何 HTTP 入口点的 WAR 文件,仅用于在 Jakarta EE 环境中引导 Spring。ApplicationContextApplicationContextApplicationContext

RAR 部署非常适合不需要 HTTP 入口点但 而仅由消息端点和计划作业组成。在这种情况下,豆类可以 使用应用程序服务器资源,例如 JTA 事务管理器和 JNDI 绑定的 JDBC 实例和 JMS 实例,还可以注册 该平台的 JMX 服务器 — 全部通过 Spring 的标准事务管理和 JNDI 和 JMX 支持设施。应用程序组件还可以与应用程序交互 服务器的 JCA 通过 Spring 的抽象。DataSourceConnectionFactoryWorkManagerTaskExecutor

有关RAR部署中涉及的配置详细信息,请参阅SpringContextResourceAdapter类的javadoc。

要将 Spring ApplicationContext 部署为 Jakarta EE RAR 文件,请执行以下操作:

  1. 包 所有应用程序类都放入一个 RAR 文件(这是一个标准的 JAR 文件,具有不同的 文件扩展名)。

  2. 将所有必需的库 JAR 添加到 RAR 存档的根目录中。

  3. 添加部署描述符(如 SpringContextResourceAdapter 的 javadoc 中所示) 以及相应的 Spring XML Bean 定义文件(通常为 )。META-INF/ra.xmlMETA-INF/applicationContext.xml

  4. 将生成的 RAR 文件放入 应用程序服务器的部署目录。

这种RAR部署单元通常是独立的。它们不会公开组件 对外界,甚至对同一应用程序的其他模块也不行。与 基于 RAR 通常通过与其共享的 JMS 目标发生 其他模块。例如,基于 RAR 的还可以安排一些作业 或对文件系统中的新文件(或类似文件)做出反应。如果它需要允许同步 从外部访问,它可以(例如)导出 RMI 端点,这些端点可用于 由同一台机器上的其他应用程序模块提供。ApplicationContextApplicationContext