可观测性支持

Micrometer 定义了一个 可观测性概念,可在应用程序中同时启用 Metrics 和 Traces。Metrics 支持提供了一种创建计时器、量规或计数器的方法,用于收集应用程序运行时行为的统计信息。Metrics 可以帮助您跟踪错误率、使用模式、性能等。Traces 提供整个系统的整体视图,跨越应用程序边界;您可以放大特定用户请求,并跟踪它们在应用程序中的完整完成过程。

如果配置了 ObservationRegistry,Spring Framework 会在其自身代码库的各个部分进行检测,以发布可观测性数据。您可以了解更多关于在 Spring Boot 中配置可观测性基础设施的信息。

生成的观测数据列表

Spring Framework 对各种功能进行了检测,以实现可观测性。如本节开头所述,观测数据可以根据配置生成定时器 Metrics 和/或 Traces。

表 1. Spring Framework 生成的观测数据
观测数据名称 描述

"http.client.requests"

HTTP 客户端交换所花费的时间

"http.server.requests"

Framework 级别 HTTP 服务器交换的处理时间

"jms.message.publish"

消息生产者向目标发送 JMS 消息所花费的时间。

"jms.message.process"

消息消费者先前接收到的 JMS 消息的处理时间。

"tasks.scheduled.execution"

@Scheduled 任务执行的处理时间

观测数据使用 Micrometer 的官方命名约定,但 Metrics 名称将自动转换为监控系统后端偏好的格式 (Prometheus, Atlas, Graphite, InfluxDB 等)。

Micrometer Observation 概念

如果您不熟悉 Micrometer Observation,这里快速总结了一些您应该了解的概念。

  • Observation 是应用程序中发生事件的实际记录。这由 ObservationHandler 实现处理,以生成指标或跟踪。

  • 每个观测数据都有一个相应的 ObservationContext 实现;这种类型包含所有相关信息,用于提取其元数据。对于 HTTP 服务器观测数据,上下文实现可以包含 HTTP 请求、HTTP 响应、处理过程中抛出的任何异常等。

  • 每个 Observation 都包含 KeyValues 元数据。对于 HTTP 服务器观测数据,这可以是 HTTP 请求方法、HTTP 响应状态等。此元数据由 ObservationConvention 实现贡献,它们应该声明它们支持的 ObservationContext 类型。

  • 如果 KeyValue 元组可能值的数量很少且有限,则称 KeyValues 为“低基数”(HTTP 方法是一个很好的例子)。低基数值仅用于指标。相反,“高基数”值是无界的(例如,HTTP 请求 URI),仅用于跟踪。

  • ObservationDocumentation 记录特定领域中的所有观测数据,列出了预期的键名及其含义。

配置观测数据

全局配置选项可在 ObservationRegistry#observationConfig() 级别获得。每个检测组件将提供两个扩展点

  • 设置 ObservationRegistry;如果未设置,将不会记录观测数据并作为无操作处理

  • 提供自定义 ObservationConvention 以更改默认观测数据名称和提取的 KeyValues

使用自定义观测数据约定

以使用 ServerHttpObservationFilter 对 Spring MVC 的 "http.server.requests" 指标进行检测为例。该观测数据使用带有 ServerRequestObservationContextServerRequestObservationConvention;可以在 Servlet 过滤器上配置自定义约定。如果您想自定义观测数据产生的元数据,可以根据您的需求扩展 DefaultServerRequestObservationConvention

import io.micrometer.common.KeyValue;
import io.micrometer.common.KeyValues;

import org.springframework.http.server.observation.DefaultServerRequestObservationConvention;
import org.springframework.http.server.observation.ServerRequestObservationContext;

public class ExtendedServerRequestObservationConvention extends DefaultServerRequestObservationConvention {

	@Override
	public KeyValues getLowCardinalityKeyValues(ServerRequestObservationContext context) {
		// here, we just want to have an additional KeyValue to the observation, keeping the default values
		return super.getLowCardinalityKeyValues(context).and(custom(context));
	}

	private KeyValue custom(ServerRequestObservationContext context) {
		return KeyValue.of("custom.method", context.getCarrier().getMethod());
	}

}

如果您想要完全控制,可以为您感兴趣的观测数据实现完整的约定契约

import java.util.Locale;

import io.micrometer.common.KeyValue;
import io.micrometer.common.KeyValues;

import org.springframework.http.server.observation.ServerHttpObservationDocumentation;
import org.springframework.http.server.observation.ServerRequestObservationContext;
import org.springframework.http.server.observation.ServerRequestObservationConvention;

public class CustomServerRequestObservationConvention implements ServerRequestObservationConvention {

	@Override
	public String getName() {
		// will be used as the metric name
		return "http.server.requests";
	}

	@Override
	public String getContextualName(ServerRequestObservationContext context) {
		// will be used for the trace name
		return "http " + context.getCarrier().getMethod().toLowerCase(Locale.ROOT);
	}

	@Override
	public KeyValues getLowCardinalityKeyValues(ServerRequestObservationContext context) {
		return KeyValues.of(method(context), status(context), exception(context));
	}


	@Override
	public KeyValues getHighCardinalityKeyValues(ServerRequestObservationContext context) {
		return KeyValues.of(httpUrl(context));
	}

	private KeyValue method(ServerRequestObservationContext context) {
		// You should reuse as much as possible the corresponding ObservationDocumentation for key names
		return KeyValue.of(ServerHttpObservationDocumentation.LowCardinalityKeyNames.METHOD, context.getCarrier().getMethod());
	}

	// status(), exception(), httpUrl()...

	private KeyValue status(ServerRequestObservationContext context) {
		return KeyValue.of(ServerHttpObservationDocumentation.LowCardinalityKeyNames.STATUS, String.valueOf(context.getResponse().getStatus()));
	}

	private KeyValue exception(ServerRequestObservationContext context) {
		String exception = (context.getError() != null ? context.getError().getClass().getSimpleName() : KeyValue.NONE_VALUE);
		return KeyValue.of(ServerHttpObservationDocumentation.LowCardinalityKeyNames.EXCEPTION, exception);
	}

	private KeyValue httpUrl(ServerRequestObservationContext context) {
		return KeyValue.of(ServerHttpObservationDocumentation.HighCardinalityKeyNames.HTTP_URL, context.getCarrier().getRequestURI());
	}

}

您也可以使用自定义 ObservationFilter 实现类似目标——添加或删除观测数据的键值。过滤器不会替代默认约定,而是作为后处理组件使用。

import io.micrometer.common.KeyValue;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationFilter;

import org.springframework.http.server.observation.ServerRequestObservationContext;

public class ServerRequestObservationFilter implements ObservationFilter {

	@Override
	public Observation.Context map(Observation.Context context) {
		if (context instanceof ServerRequestObservationContext serverContext) {
			context.setName("custom.observation.name");
			context.addLowCardinalityKeyValue(KeyValue.of("project", "spring"));
			String customAttribute = (String) serverContext.getCarrier().getAttribute("customAttribute");
			context.addLowCardinalityKeyValue(KeyValue.of("custom.attribute", customAttribute));
		}
		return context;
	}
}

您可以在 ObservationRegistry 上配置 ObservationFilter 实例。

@Scheduled 任务检测

对于每个 @Scheduled 任务的执行,都会创建一个 Observation。应用程序需要在 ScheduledTaskRegistrar 上配置 ObservationRegistry 以启用观测数据的记录。这可以通过声明一个设置观测数据注册中心的 SchedulingConfigurer bean 来完成

import io.micrometer.observation.ObservationRegistry;

import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;

public class ObservationSchedulingConfigurer implements SchedulingConfigurer {

	private final ObservationRegistry observationRegistry;

	public ObservationSchedulingConfigurer(ObservationRegistry observationRegistry) {
		this.observationRegistry = observationRegistry;
	}

	@Override
	public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
		taskRegistrar.setObservationRegistry(this.observationRegistry);
	}

}

默认情况下,它使用 org.springframework.scheduling.support.DefaultScheduledTaskObservationConvention,并由 ScheduledTaskObservationContext 提供支持。您可以直接在 ObservationRegistry 上配置自定义实现。在计划方法的执行期间,当前观测数据将在 ThreadLocal 上下文或 Reactor 上下文中恢复(如果计划方法返回 MonoFlux 类型)。

默认情况下,会创建以下 KeyValues

表 2. 低基数键

名称

描述

code.function (必需)

计划执行的 Java Method 的名称。

code.namespace (必需)

持有计划方法的 bean 实例类的规范名称,对于匿名类为 "ANONYMOUS"

error (必需)

执行期间抛出的异常类名,如果没有发生异常则为 "none"

exception (已弃用)

重复 error 键,将来可能会被移除。

outcome (必需)

方法执行的结果。可以是 "SUCCESS""ERROR""UNKNOWN"(例如,如果在执行期间操作被取消)。

JMS 消息传递检测

如果 classpath 中存在 io.micrometer:micrometer-jakarta9 依赖项,Spring Framework 将使用 Micrometer 提供的 Jakarta JMS 检测。io.micrometer.jakarta9.instrument.jms.JmsInstrumentationjakarta.jms.Session 进行检测并记录相关的观测数据。

此检测将创建 2 种类型的观测数据

  • 当 JMS 消息发送到 broker 时创建 "jms.message.publish" 观测数据,通常使用 JmsTemplate

  • 当 JMS 消息由应用程序处理时创建 "jms.message.process" 观测数据,通常使用 MessageListener 或带有 @JmsListener 注解的方法。

目前没有针对 "jms.message.receive" 观测数据的检测,因为测量等待消息接收所花费的时间价值不大。这种集成通常会检测 MessageConsumer#receive 方法调用。但一旦这些调用返回,就不会测量处理时间,并且跟踪范围也无法传播到应用程序。

默认情况下,这两种观测数据共享同一组可能的 KeyValues

表 3. 低基数键

名称

描述

error

消息操作期间抛出的异常类名(或 "none")。

exception (已弃用)

重复 error 键,将来可能会被移除。

messaging.destination.temporary (必需)

目标是否为 TemporaryQueueTemporaryTopic(值:"true""false")。

messaging.operation (必需)

正在执行的 JMS 操作名称(值:"publish""process")。

表 4. 高基数键

名称

描述

messaging.message.conversation_id

JMS 消息的关联 ID。

messaging.destination.name

发送当前消息到的目标名称。

messaging.message.id

消息系统用作消息标识符的值。

JMS 消息发布检测

当 JMS 消息发送到 broker 时,会记录 "jms.message.publish" 观测数据。它们测量发送消息所花费的时间,并通过传出的 JMS 消息头传播跟踪信息。

您需要在 JmsTemplate 上配置 ObservationRegistry 以启用观测数据

import io.micrometer.observation.ObservationRegistry;
import jakarta.jms.ConnectionFactory;

import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.jms.core.JmsTemplate;

public class JmsTemplatePublish {

	private final JmsTemplate jmsTemplate;

	private final JmsMessagingTemplate jmsMessagingTemplate;

	public JmsTemplatePublish(ObservationRegistry observationRegistry, ConnectionFactory connectionFactory) {
		this.jmsTemplate = new JmsTemplate(connectionFactory);
		// configure the observation registry
		this.jmsTemplate.setObservationRegistry(observationRegistry);

		// For JmsMessagingTemplate, instantiate it with a JMS template that has a configured registry
		this.jmsMessagingTemplate = new JmsMessagingTemplate(this.jmsTemplate);
	}

	public void sendMessages() {
		this.jmsTemplate.convertAndSend("spring.observation.test", "test message");
	}

}

默认情况下,它使用 io.micrometer.jakarta9.instrument.jms.DefaultJmsPublishObservationConvention,并由 io.micrometer.jakarta9.instrument.jms.JmsPublishObservationContext 提供支持。

当侦听器方法返回响应消息时,带有 @JmsListener 注解的方法也会记录类似的观测数据。

JMS 消息处理检测

当 JMS 消息由应用程序处理时,会记录 "jms.message.process" 观测数据。它们测量处理消息所花费的时间,并通过传入的 JMS 消息头传播跟踪上下文。

大多数应用程序将使用带有 @JmsListener 注解的方法机制来处理传入消息。您需要确保在专用的 JmsListenerContainerFactory 上配置了 ObservationRegistry

import io.micrometer.observation.ObservationRegistry;
import jakarta.jms.ConnectionFactory;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;

@Configuration
@EnableJms
public class JmsConfiguration {

	@Bean
	public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(ConnectionFactory connectionFactory, ObservationRegistry observationRegistry) {
		DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
		factory.setConnectionFactory(connectionFactory);
		factory.setObservationRegistry(observationRegistry);
		return factory;
	}

}

需要一个默认的容器工厂来启用注解支持,但请注意,@JmsListener 注解可以引用特定用途的特定容器工厂 bean。在所有情况下,只有在容器工厂上配置了观测数据注册中心时,才会记录观测数据。

当消息由 MessageListener 处理时,使用 JmsTemplate 也会记录类似的观测数据。此类监听器在会话回调(参见 JmsTemplate.execute(SessionCallback<T>))中设置在 MessageConsumer 上。

默认情况下,此观测数据使用 io.micrometer.jakarta9.instrument.jms.DefaultJmsProcessObservationConvention,并由 io.micrometer.jakarta9.instrument.jms.JmsProcessObservationContext 提供支持。

HTTP 服务器检测

HTTP 服务器交换观测数据以名称 "http.server.requests" 创建,适用于 Servlet 和响应式应用程序。

Servlet 应用

应用程序需要在其应用程序中配置 org.springframework.web.filter.ServerHttpObservationFilter Servlet 过滤器。默认情况下,它使用 org.springframework.http.server.observation.DefaultServerRequestObservationConvention,并由 ServerRequestObservationContext 提供支持。

只有当 Exception 未被 web 框架处理并冒泡到 Servlet 过滤器时,才会将观测数据记录为错误。通常,Spring MVC 的 @ExceptionHandlerProblemDetail 支持处理的所有异常都不会随观测数据一起记录。您可以在请求处理的任何时候,自行在 ObservationContext 上设置 error 字段

import jakarta.servlet.http.HttpServletRequest;

import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.filter.ServerHttpObservationFilter;

@Controller
public class UserController {

	@ExceptionHandler(MissingUserException.class)
	ResponseEntity<Void> handleMissingUser(HttpServletRequest request, MissingUserException exception) {
		// We want to record this exception with the observation
		ServerHttpObservationFilter.findObservationContext(request)
				.ifPresent(context -> context.setError(exception));
		return ResponseEntity.notFound().build();
	}

	static class MissingUserException extends RuntimeException {
	}

}
由于检测是在 Servlet 过滤器级别完成的,因此观测范围仅涵盖在此之后排序的过滤器以及请求的处理。通常,Servlet 容器的错误处理在较低级别执行,不会有任何活动的观测数据或 span。对于此用例,需要特定于容器的实现,例如 Tomcat 的 org.apache.catalina.Valve;这超出了本项目范围。

默认情况下,会创建以下 KeyValues

表 5. 低基数键

名称

描述

error (必需)

交换期间抛出的异常类名,如果没有发生异常则为 "none"

exception (已弃用)

重复 error 键,将来可能会被移除。

method (必需)

HTTP 请求方法的名称,如果不是已知方法则为 "none"

outcome (必需)

HTTP 服务器交换的结果。

status (必需)

HTTP 响应原始状态码,如果未创建响应则为 "UNKNOWN"

uri (必需)

匹配 handler 的 URI 模式(如果可用),对于 3xx 响应回退到 REDIRECTION,对于 404 响应回退到 NOT_FOUND,对于没有路径信息的请求回退到 root,对于所有其他请求回退到 UNKNOWN

表 6. 高基数键

名称

描述

http.url (必需)

HTTP 请求 URI。

响应式应用

应用程序需要使用 MeterRegistry 配置 WebHttpHandlerBuilder 以启用服务器检测。这可以在 WebHttpHandlerBuilder 上完成,如下所示

import io.micrometer.observation.ObservationRegistry;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;

@Configuration(proxyBeanMethods = false)
public class HttpHandlerConfiguration {

	private final ApplicationContext applicationContext;

	public HttpHandlerConfiguration(ApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
	}

	@Bean
	public HttpHandler httpHandler(ObservationRegistry registry) {
		return WebHttpHandlerBuilder.applicationContext(this.applicationContext)
				.observationRegistry(registry)
				.build();
	}
}

默认情况下,它使用 org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention,并由 ServerRequestObservationContext 提供支持。

只有当 Exception 未被应用程序 Controller 处理时,才会将观测数据记录为错误。通常,Spring WebFlux 的 @ExceptionHandlerProblemDetail 支持处理的所有异常都不会随观测数据一起记录。您可以在请求处理的任何时候,自行在 ObservationContext 上设置 error 字段

import org.springframework.http.ResponseEntity;
import org.springframework.http.server.reactive.observation.ServerRequestObservationContext;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.server.ServerWebExchange;

@Controller
public class UserController {

	@ExceptionHandler(MissingUserException.class)
	ResponseEntity<Void> handleMissingUser(ServerWebExchange exchange, MissingUserException exception) {
		// We want to record this exception with the observation
		ServerRequestObservationContext.findCurrent(exchange.getAttributes())
				.ifPresent(context -> context.setError(exception));
		return ResponseEntity.notFound().build();
	}

	static class MissingUserException extends RuntimeException {
	}

}

默认情况下,会创建以下 KeyValues

表 7. 低基数键

名称

描述

error (必需)

交换期间抛出的异常类名,如果没有发生异常则为 "none"

exception (已弃用)

重复 error 键,将来可能会被移除。

method (必需)

HTTP 请求方法的名称,如果不是已知方法则为 "none"

outcome (必需)

HTTP 服务器交换的结果。

status (必需)

HTTP 响应原始状态码,如果未创建响应则为 "UNKNOWN"

uri (必需)

匹配 handler 的 URI 模式(如果可用),对于 3xx 响应回退到 REDIRECTION,对于 404 响应回退到 NOT_FOUND,对于没有路径信息的请求回退到 root,对于所有其他请求回退到 UNKNOWN

表 8. 高基数键

名称

描述

http.url (必需)

HTTP 请求 URI。

HTTP 客户端检测

HTTP 客户端交换观测数据以名称 "http.client.requests" 创建,适用于阻塞和响应式客户端。此观测数据测量整个 HTTP 请求/响应交换,从连接建立到请求体反序列化。与服务器端不同,检测直接在客户端实现,因此唯一需要的步骤是在客户端上配置 ObservationRegistry

RestTemplate

应用程序必须在 RestTemplate 实例上配置 ObservationRegistry 以启用检测;否则,观测数据将作为 "no-ops"(无操作)处理。Spring Boot 会自动配置已设置观测数据注册中心的 RestTemplateBuilder bean。

默认情况下,检测使用 org.springframework.http.client.observation.ClientRequestObservationConvention,并由 ClientRequestObservationContext 提供支持。

表 9. 低基数键

名称

描述

method (必需)

HTTP 请求方法的名称,如果不是已知方法则为 "none"

uri (必需)

用于 HTTP 请求的 URI 模板,如果未提供则为 "none"。URI 的协议、主机和端口部分不予考虑。

client.name (必需)

从请求 URI 主机派生的客户端名称。

status (必需)

HTTP 响应原始状态码,IOException 情况下为 "IO_ERROR",如果未收到响应则为 "CLIENT_ERROR"

outcome (必需)

HTTP 客户端交换的结果。

error (必需)

交换期间抛出的异常类名,如果没有发生异常则为 "none"

exception (已弃用)

重复 error 键,将来可能会被移除。

表 10. 高基数键

名称

描述

http.url (必需)

HTTP 请求 URI。

RestClient

应用程序必须在 RestClient.Builder 上配置 ObservationRegistry 以启用检测;否则,观测数据将作为 "no-ops"(无操作)处理。

默认情况下,检测使用 org.springframework.http.client.observation.ClientRequestObservationConvention,并由 ClientRequestObservationContext 提供支持。

表 11. 低基数键

名称

描述

method (必需)

HTTP 请求方法的名称,如果无法创建请求则为 "none"

uri (必需)

用于 HTTP 请求的 URI 模板,如果未提供则为 "none"。URI 的协议、主机和端口部分不予考虑。

client.name (必需)

从请求 URI 主机派生的客户端名称。

status (必需)

HTTP 响应原始状态码,IOException 情况下为 "IO_ERROR",如果未收到响应则为 "CLIENT_ERROR"

outcome (必需)

HTTP 客户端交换的结果。

error (必需)

交换期间抛出的异常类名,如果没有发生异常则为 "none"

exception (已弃用)

重复 error 键,将来可能会被移除。

表 12. 高基数键

名称

描述

http.url (必需)

HTTP 请求 URI。

WebClient

应用程序必须在 WebClient.Builder 上配置 ObservationRegistry 以启用检测;否则,观测数据将作为 "no-ops"(无操作)处理。Spring Boot 会自动配置已设置观测数据注册中心的 WebClient.Builder bean。

默认情况下,检测使用 org.springframework.web.reactive.function.client.ClientRequestObservationConvention,并由 ClientRequestObservationContext 提供支持。

表 13. 低基数键

名称

描述

method (必需)

HTTP 请求方法的名称,如果不是已知方法则为 "none"

uri (必需)

用于 HTTP 请求的 URI 模板,如果未提供则为 "none"。URI 的协议、主机和端口部分不予考虑。

client.name (必需)

从请求 URI 主机派生的客户端名称。

status (必需)

HTTP 响应原始状态码,IOException 情况下为 "IO_ERROR",如果未收到响应则为 "CLIENT_ERROR"

outcome (必需)

HTTP 客户端交换的结果。

error (必需)

交换期间抛出的异常类名,如果没有发生异常则为 "none"

exception (已弃用)

重复 error 键,将来可能会被移除。

表 14. 高基数键

名称

描述

http.url (必需)

HTTP 请求 URI。

应用事件和 @EventListener

Spring Framework 不为@EventListener 调用贡献 Observation,因为它们不具备此类检测的正确语义。默认情况下,事件发布和处理是同步的,并且在同一线程上完成。这意味着在该任务执行期间,ThreadLocals 和日志上下文将与事件发布者相同。

如果应用程序全局配置了一个自定义的 ApplicationEventMulticaster,并且其策略是在不同的线程上调度事件处理,那么这种情况就不再适用。所有 @EventListener 方法将在不同的线程上处理,而不是在主要的事件发布线程上。在这种情况下,Micrometer Context Propagation library 可以帮助传播这些值并更好地关联事件的处理。应用程序可以配置所选的 TaskExecutor 来使用一个 ContextPropagatingTaskDecorator,它会装饰任务并传播上下文。为了使其工作,io.micrometer:context-propagation 库必须存在于 classpath 中

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.support.ContextPropagatingTaskDecorator;

@Configuration
public class ApplicationEventsConfiguration {

	@Bean(name = "applicationEventMulticaster")
	public SimpleApplicationEventMulticaster simpleApplicationEventMulticaster() {
		SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();
		SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
		// decorate task execution with a decorator that supports context propagation
		taskExecutor.setTaskDecorator(new ContextPropagatingTaskDecorator());
		eventMulticaster.setTaskExecutor(taskExecutor);
		return eventMulticaster;
	}

}

类似地,如果通过在每个 @EventListener 注解的方法上添加 @Async 来在本地进行异步选择,您可以通过引用其限定符来选择一个传播上下文的 TaskExecutor。考虑到以下配置了专用任务装饰器的 TaskExecutor bean 定义

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
import org.springframework.core.task.support.ContextPropagatingTaskDecorator;

@Configuration
public class EventAsyncExecutionConfiguration {

	@Bean(name = "propagatingContextExecutor")
	public TaskExecutor propagatingContextExecutor() {
		SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
		// decorate task execution with a decorator that supports context propagation
		taskExecutor.setTaskDecorator(new ContextPropagatingTaskDecorator());
		return taskExecutor;
	}

}

使用 @Async 和相关限定符注解事件监听器将实现类似的上下文传播效果

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
public class EmailNotificationListener {

	private final Log logger = LogFactory.getLog(EmailNotificationListener.class);

	@EventListener(EmailReceivedEvent.class)
	@Async("propagatingContextExecutor")
	public void emailReceived(EmailReceivedEvent event) {
		// asynchronously process the received event
		// this logging statement will contain the expected MDC entries from the propagated context
		logger.info("email has been received");
	}

}