Observability Support

Micrometer 在应用程序中定义了一个 Observation concept that enables both Metrics and Traces。度量支持提供了一种创建计时器、测量器或计数器的方法,用于收集有关应用程序运行时行为的统计信息。度量可以帮助你跟踪错误率、使用模式、性能等。跟踪提供整个系统的一个整体视图,跨越应用程序边界;你可以深入研究特定的用户请求,并跟踪它们在各个应用程序中的整个完成情况。 如果配置了`ObservationRegistry`,Spring Framework 会对自己的代码库的各个部分进行检测以发布观测结果。你可以了解更多有关[在 Spring Boot 中配置可观测性基础设施](https://spring.io/docs/spring-boot/docs/current/reference/html/actuator.html#actuator.metrics)的信息。

List of produced Observations

Spring Framework 检测各种功能以进行观测。如at the beginning of this section所述,观测可以根据配置生成计时器度量和/或跟踪。

Table 1. Observations produced by Spring Framework
Observation name Description

"http.client.requests"

用于 HTTP 客户端交换的时间

"http.server.requests"

在 Framework 层处理 HTTP 服务器交换的时间

"jms.message.publish"

由消息生产者将 JMS 消息发送到目标所花的时间。

"jms.message.process"

对消息使用者先前接收的 JMS 消息进行处理的时间。

"tasks.scheduled.execution"

执行 @Scheduled 任务的处理时间

观察结果使用 Micrometer 的官方命名约定,但指标名称将自动转换 to the format preferred by the monitoring system backend(Prometheus、Atlas、Graphite、InfluxDB…​)。

Micrometer Observation concepts

如果您不熟悉 Micrometer 观察,以下是对您应该了解的概念的快速总结。

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

  • 每个观察都有一个相应的 ObservationContext 实现;此类型包含用于提取其元数据的相关信息。在 HTTP 服务器观察的情况下,上下文实现可以保存 HTTP 请求、HTTP 响应、处理过程中引发的任何异常,等等。

  • 每个 Observation 保存 KeyValues 元数据。在 HTTP 服务器观察的情况下,这可以是 HTTP 请求方法、HTTP 响应状态等等。此元数据由 ObservationConvention 实现提供,这些实现应声明它们支持的 ObservationContext 类型。

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

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

Configuring Observations

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

  • 设置 ObservationRegistry;如果未设置,则不会记录观察,并且这些观察将是空操作

  • 提供自定义 ObservationConvention 来更改默认观察名称和提取的 KeyValues

Using custom Observation conventions

我们以 Spring MVC “http.server.requests”指标检测(使用“ServerHttpObservationFilter”)为例。此观察使用具有“ServerRequestObservationContext”的“ServerRequestObservationConvention”;自定义约定可以在 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 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();
	}

	@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 tasks instrumentation

为 xref:integration/scheduling.adoc#scheduling-enable-annotation-support[each execution of an @Scheduled 任务创建了观察。应用程序需要在 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);
	}

}

默认情况下,它使用由 ScheduledTaskObservationContext 支持的 org.springframework.scheduling.support.DefaultScheduledTaskObservationConvention。你可以在 ObservationRegistry 上直接配置自定义实现。在执行计划方法时,当前观察结果会还原到 ThreadLocal 上下文或 Reactor 上下文(如果计划方法返回 MonoFlux 类型)。

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

Table 2. Low cardinality Keys

Name

Description

code.function (required)

已计划执行的 Java Method 的名称。

code.namespace (required)

包含计划方法的 bean 实例的类的规范名称,或匿名的类的 "ANONYMOUS"

error (required)

执行过程中抛出的异常的类名称,或如果未发生异常则为 "none"

exception (deprecated)

复制 error 键,且将来可能会被删除。

outcome (required)

方法执行的结果。可能是 "SUCCESS""ERROR""UNKNOWN"(例如,如果在执行过程中操作被取消)。

JMS messaging instrumentation

如果 classpath 中存在 io.micrometer:micrometer-jakarta9 依赖项,Spring 框架会使用 Micrometer 提供的 Jakarta JMS 检测器。io.micrometer.jakarta9.instrument.jms.JmsInstrumentation 检测器检测 jakarta.jms.Session 并在相关观察结果中记录该检测器。

此检测器将创建 2 种类型的观察结果:

  • "jms.message.publish" 当 JMS 消息发送给代理时,通常使用 JmsTemplate

  • "jms.message.process" 当应用程序处理 JMS 消息时,通常使用带有 MessageListener@JmsListener 注释的方法。

目前没有用于 "jms.message.receive" 观测的监测,因为测量接收消息时等待所花费的时间价值不大。此类集成通常会监测 MessageConsumer#receive 方法调用。但在这些方法返回后,不会测量处理时间而且无法将跟踪范围传播到应用中。

默认情况下,这两个观察结果共享同一组可能的 KeyValues

Table 3. Low cardinality Keys

Name

Description

error

消息传递操作期间引发的异常的类名(或 "none")。

exception (deprecated)

复制 error 键,且将来可能会被删除。

messaging.destination.temporary (required)

目的地是 TemporaryQueue 还是 TemporaryTopic(值:"true""false")。

messaging.operation (required)

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

Table 4. High cardinality Keys

Name

Description

messaging.message.conversation_id

JMS 消息的相关性 ID。

messaging.destination.name

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

messaging.message.id

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

JMS message Publication instrumentation

当 JMS 消息发送到代理时,将记录 "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.JmsPublishObservationContext 支持的 io.micrometer.jakarta9.instrument.jms.DefaultJmsPublishObservationConvention

JMS message Processing instrumentation

当应用程序处理 JMS 消息时,将记录 "jms.message.process" 观察结果。它们会衡量处理消息所需的时间,并使用传出的 JMS 消息标头传播跟踪上下文。

大多数应用程序将使用 @JmsListener annotated methods 机制来处理传入消息。你需要确保在专用 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;
	}

}

一个 default container factory is required to enable the annotation support, 但请注意,@JmsListener 注释可以引用特定目的的特定容器工厂 bean。在所有情况下,仅当在容器工厂上配置了观察注册中心时才记录观察结果。

MessageListener 处理消息时,会在 JmsTemplate 中记录类似的观察结果。此类侦听器是在会话回调中的 MessageConsumer 上设置的(请参见 JmsTemplate.execute(SessionCallback<T>))。

默认情况下,此观察结果使用由 io.micrometer.jakarta9.instrument.jms.JmsProcessObservationContext 支持的 io.micrometer.jakarta9.instrument.jms.DefaultJmsProcessObservationConvention

HTTP Server instrumentation

HTTP 服务器交换观察结果使用 Servlet 和 Reactive 应用程序的名称 "http.server.requests" 创建。

Servlet applications

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

这只会将观测结果记录为错误,如果 web 框架没有处理`Exception`,并且已上升到 Servlet 过滤器。通常,Spring MVC 的`@ExceptionHandler`和ProblemDetail support处理的所有异常都不会记录在观测结果中。你可以在请求处理过程中的任何时候自行设置`ObservationContext`上的错误字段:

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 Filter 级别执行监测,观测范围仅涵盖此 Filter 之后指定的 Filter 以及请求处理。通常,Servlet 容器错误处理是在较低级别执行的,且不会有任何活动观测或范围。对于此用例,需要一个特定于容器的实现,例如 org.apache.catalina.Valve 用于 Tomcat;这不在此项目范围内。

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

Table 5. Low cardinality Keys

Name

Description

error (required)

交换期间引发的异常的类名,或 "none"(如果未发生异常)。

exception (deprecated)

复制 error 键,且将来可能会被删除。

method (required)

HTTP 请求方法的名称或 "none"(如果不是一个众所周知的名称)。

outcome (required)

HTTP 服务器交换的结果。

status (required)

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

uri (required)

匹配处理程序的 URI 模式的模式,如果可用,则设置为 REDIRECTION 以获取 3xx 响应,设置为 NOT_FOUND 以获取 404 响应,设置为 root 以获取无路径信息的请求,设置为 UNKNOWN 以获取所有其他请求。

Table 6. High cardinality Keys

Name

Description

http.url (required)

HTTP request URI.

Reactive applications

应用程序需要使用 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 未被应用程序控制器处理时,才会将观察记录为错误。通常,由 Spring WebFlux 的 @ExceptionHandler 和 xref:web.adoc#webflux-ann-rest-exceptions[ProblemDetail 支持处理的所有异常都不会记录到观察中。在请求处理期间的任何时候,您都可以自己设置 ObservationContext 上的错误字段:

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

Table 7. Low cardinality Keys

Name

Description

error (required)

交换期间引发的异常的类名,或 "none"(如果未发生异常)。

exception (deprecated)

复制 error 键,且将来可能会被删除。

method (required)

HTTP 请求方法的名称或 "none"(如果不是一个众所周知的名称)。

outcome (required)

HTTP 服务器交换的结果。

status (required)

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

uri (required)

匹配处理程序的 URI 模式的模式,如果可用,则设置为 REDIRECTION 以获取 3xx 响应,设置为 NOT_FOUND 以获取 404 响应,设置为 root 以获取无路径信息的请求,设置为 UNKNOWN 以获取所有其他请求。

Table 8. High cardinality Keys

Name

Description

http.url (required)

HTTP request URI.

HTTP Client Instrumentation

HTTP 客户端交换观察结果使用名称 "http.client.requests" 为阻塞和反应客户端创建。与服务器对应项不同,仪表化直接在客户端中实现,因此唯一必需的步骤是在客户端上配置 ObservationRegistry

RestTemplate

应用程序必须在 RestTemplate 实例上配置 ObservationRegistry 以启用仪表化;如果没有它,观察结果就是“无操作”。Spring Boot 将自动配置已设置观察结果注册表的 RestTemplateBuilder bean。

仪表化默认情况下使用 org.springframework.http.client.observation.ClientRequestObservationConvention,该约定由 ClientRequestObservationContext 提供支持。

Table 9. Low cardinality Keys

Name

Description

method (required)

HTTP 请求方法的名称或 "none"(如果不是一个众所周知的名称)。

uri (required)

用于 HTTP 请求的 URI 模板,如果没有提供,则为 "none"。仅考虑 URI 的路径部分。

client.name (required)

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

status (required)

HTTP 响应原始状态代码或在 IOException 情况下为 "IO_ERROR",或在未收到响应的情况下为 "CLIENT_ERROR"

outcome (required)

HTTP 客户端交换结果。

error (required)

交换期间引发的异常的类名,或 "none"(如果未发生异常)。

exception (deprecated)

复制 error 键,且将来可能会被删除。

Table 10. High cardinality Keys

Name

Description

http.url (required)

HTTP request URI.

RestClient

应用程序必须在 RestClient.Builder 上配置 ObservationRegistry 以启用仪表化;如果没有它,观察结果就是“无操作”。

仪表化默认情况下使用 org.springframework.http.client.observation.ClientRequestObservationConvention,该约定由 ClientRequestObservationContext 提供支持。

Table 11. Low cardinality Keys

Name

Description

method (required)

HTTP 请求方法名称或在无法创建请求的情况下为 "none"

uri (required)

用于 HTTP 请求的 URI 模板,如果没有提供,则为 "none"。仅考虑 URI 的路径部分。

client.name (required)

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

status (required)

HTTP 响应原始状态代码或在 IOException 情况下为 "IO_ERROR",或在未收到响应的情况下为 "CLIENT_ERROR"

outcome (required)

HTTP 客户端交换结果。

error (required)

交换期间引发的异常的类名,或 "none"(如果未发生异常)。

exception (deprecated)

复制 error 键,且将来可能会被删除。

Table 12. High cardinality Keys

Name

Description

http.url (required)

HTTP request URI.

WebClient

应用程序必须在 WebClient 构建器上配置 ObservationRegistry 以启用仪表化;如果没有它,观察结果就是“无操作”。Spring Boot 将自动配置已设置观察结果注册表的 WebClient.Builder bean。

仪表化默认情况下使用 org.springframework.web.reactive.function.client.ClientRequestObservationConvention,该约定由 ClientRequestObservationContext 提供支持。

Table 13. Low cardinality Keys

Name

Description

method (required)

HTTP 请求方法的名称或 "none"(如果不是一个众所周知的名称)。

uri (required)

用于 HTTP 请求的 URI 模板,如果没有提供,则为 "none"。仅考虑 URI 的路径部分。

client.name (required)

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

status (required)

HTTP 响应原始状态代码或在 IOException 情况下为 "IO_ERROR",或在未收到响应的情况下为 "CLIENT_ERROR"

outcome (required)

HTTP 客户端交换结果。

error (required)

交换期间引发的异常的类名,或 "none"(如果未发生异常)。

exception (deprecated)

复制 error 键,且将来可能会被删除。

Table 14. High cardinality Keys

Name

Description

http.url (required)

HTTP request URI.

Application Events and @EventListener

Spring 框架不为 xref:core/beans/context-introduction.adoc#context-functionality-events-annotation[@EventListener 调用提供观察,因为它们没有此类检测的正确语义。默认情况下,事件发布和处理是同步进行的,并且在同一线程上。这意味着在执行该任务期间,ThreadLocals 和日志记录上下文将与事件发布方相同。

如果应用程序通过一种将事件处理计划到不同线程中的策略来全局配置一个自定义 ApplicationEventMulticaster,则这将不再成立。所有 @EventListener 方法都将在不同的线程中进行处理,而不是主事件发布线程。在这些情况下, Micrometer Context Propagation library 可以帮助传播此类值并更好地关联事件处理。应用程序可以配置所选 TaskExecutor 以使用装饰任务和传播上下文的 ContextPropagatingTaskDecorator。要实现此操作,类路径中必须存在 io.micrometer:context-propagation 库:

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。给定以下 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");
	}

}