可观察性支持

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

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

已生成观察结果的列表

Spring 框架为可观察性检测了各种功能。如本节开头所述,观察结果可以根据配置生成计时器指标和/或跟踪。

表 1. Spring 框架生成的观察结果
观察结果名称 描述

"http.client.requests"

HTTP 客户端交换花费的时间

"http.server.requests"

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

"jms.message.publish"

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

"jms.message.process"

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

"tasks.scheduled.execution"

@Scheduled 任务执行的处理时间

观察结果使用 Micrometer 的官方命名约定,但指标名称将自动转换为监控系统后端首选的格式(Prometheus、Atlas、Graphite、InfluxDB 等)。

Micrometer 观察概念

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

  • 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 任务的执行创建一个观察结果。应用程序需要在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 消息监控

如果类路径上存在io.micrometer:micrometer-jakarta9依赖项,则 Spring Framework 使用 Micrometer 提供的 Jakarta JMS 监控。io.micrometer.jakarta9.instrument.jms.JmsInstrumentation 会监控 jakarta.jms.Session 并记录相关的观测值。

此监控将创建 2 种类型的观测值

  • 当 JMS 消息发送到代理时(通常使用 JmsTemplate),会创建"jms.message.publish"观测值。

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

目前还没有 "jms.message.receive" 观测值的监控,因为衡量等待接收消息所花费的时间意义不大。此类集成通常会监控 MessageConsumer#receive 方法调用。但是,一旦这些方法返回,就不会测量处理时间,并且跟踪范围无法传播到应用程序。

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

表 3. 低基数键

名称

描述

错误

在消息传递操作期间抛出的异常的类名(或“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 消息发送到代理时,会记录"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 记录类似的观测值。此类侦听器在会话回调中设置在 MessageConsumer 上(请参阅 JmsTemplate.execute(SessionCallback<T>))。

此观测值默认使用 io.micrometer.jakarta9.instrument.jms.DefaultJmsProcessObservationConvention,由 io.micrometer.jakarta9.instrument.jms.JmsProcessObservationContext 支持。

HTTP 服务器监控

HTTP 服务器交换观测值使用名称"http.server.requests"为 Servlet 和 Reactive 应用程序创建。

Servlet 应用程序

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

只有在 Web 框架未处理 Exception 且已冒泡到 Servlet 过滤器时,此操作才会将观测值记录为错误。通常,由 Spring MVC 的 @ExceptionHandlerProblemDetail 支持处理的所有异常都不会使用观测值记录。您可以在请求处理过程中的任何时间自行在 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 过滤器级别完成的,因此观测值范围仅涵盖在此过滤器之后排序的过滤器以及请求的处理。通常,Servlet 容器错误处理是在较低级别执行的,并且没有任何活动的观测值或跨度。对于此用例,需要容器特定的实现,例如 Tomcat 的 org.apache.catalina.Valve;这不在本项目的范围内。

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

表 5. 低基数键

名称

描述

error (必需)

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

exception (已弃用)

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

method (必填)

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

outcome (必需)

HTTP 服务器交换的结果。

status (必填)

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

uri (必填)

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

表 6. 高基数键

名称

描述

http.url (必填)

HTTP 请求 URI。

Reactive 应用程序

应用程序需要使用 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 的 @ExceptionHandlerProblemDetail 支持处理的所有异常都不会使用观测值记录。您可以在请求处理过程中的任何时间自行在 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

表 7. 低基数键

名称

描述

error (必需)

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

exception (已弃用)

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

method (必填)

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

outcome (必需)

HTTP 服务器交换的结果。

status (必填)

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

uri (必填)

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

表 8. 高基数键

名称

描述

http.url (必填)

HTTP 请求 URI。

HTTP 客户端监控

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

RestTemplate

应用程序必须在 RestTemplate 实例上配置 ObservationRegistry 以启用监控;没有它,观测值将是“无操作”。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 以启用监控;没有它,观测值将是“无操作”。

监控默认使用 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 生成器上配置 ObservationRegistry 以启用监控;没有它,观测值将是“无操作”。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 调用贡献观测值,因为它们不具有此类监控的正确语义。默认情况下,事件发布和处理是同步地在同一线程上完成的。这意味着在该任务执行期间,ThreadLocals 和日志记录上下文将与事件发布者相同。

如果应用程序全局配置了一个自定义的 ApplicationEventMulticaster,并使用在不同线程上调度事件处理的策略,则情况不再如此。所有 @EventListener 方法都将在另一个线程上处理,该线程位于主事件发布线程之外。在这些情况下,Micrometer 上下文传播库可以帮助传播此类值,并更好地关联事件的处理。应用程序可以配置所选的 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 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");
	}

}