可观察性支持

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

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

已生成观察结果的列表

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

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

"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 类型。

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

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

配置观察结果

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

  • 设置 ObservationRegistry;如果未设置,则不会记录观察结果,并且将是无操作的

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

使用自定义观察结果约定

以 Spring MVC 的“http.server.requests”指标检测为例,使用 ServerHttpObservationFilter。此观察结果使用 ServerRequestObservationConventionServerRequestObservationContext;可以在 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 任务检测

每个 @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 框架将使用 Micrometer 提供的 Jakarta JMS 检测。io.micrometer.jakarta9.instrument.jms.JmsInstrumentation 检测 jakarta.jms.Session 并记录相关观察结果。

此检测将创建两种类型的观察结果

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

  • 当 JMS 消息由应用程序处理时(通常使用 MessageListener 或带 @JmsListener 注解的方法),"jms.message.process"

目前没有 "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 消息发送到代理时,会记录 "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 支持。

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 支持。

只有当 `Exception` 未被 Web 框架处理并冒泡到 Servlet 过滤器时,才会将观察结果记录为错误。通常,由 Spring MVC 的 `@ExceptionHandler` 和 `ProblemDetail` 支持 处理的所有异常都不会记录在观察结果中。您可以在请求处理过程中的任何时间点,自行在 `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。

响应式应用程序

应用程序需要使用 `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 响应原始状态代码,或"IO_ERROR"(如果发生IOException),或"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 响应原始状态代码,或"IO_ERROR"(如果发生IOException),或"CLIENT_ERROR"(如果未收到响应)。

outcome (必填)

HTTP 客户端交换的结果。

error (必填)

交换过程中抛出的异常的类名,如果未发生异常,则为 `“none”`。

exception (已弃用)

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

表 12. 高基数键

名称

描述

http.url (必需)

HTTP 请求 URI。

WebClient

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

默认情况下,Instrumentation 使用 org.springframework.web.reactive.function.client.ClientRequestObservationConvention,由 ClientRequestObservationContext 支持。

表 13. 低基数键

名称

描述

method (必需)

HTTP 请求方法的名称,如果方法未知,则为 `“none”`。

uri (必需)

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

client.name (必需)

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

status (必需)

HTTP 响应原始状态代码,或"IO_ERROR"(如果发生IOException),或"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");
	}

}