可观测性支持

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

如果配置了 ObservationRegistry,Spring Framework 会检测其自身代码库的各个部分以发布观测。您可以了解更多关于在 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 类型。

  • 如果 KeyValue 元组的可能值数量有限且有界(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. 低基数键

名称

描述

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

当响应消息从监听器方法返回时,@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 服务器检测

对于 Servlet 和 Reactive 应用程序,HTTP 服务器交换观测以 "http.server.requests" 命名,如果使用 OpenTelemetry 约定,则命名为 "http.server.request.duration"

Servlet 应用程序

应用程序需要在其应用程序中配置 org.springframework.web.filter.ServerHttpObservationFilter Servlet 过滤器。

这仅在 Exception 未被 Web 框架处理并冒泡到 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;这不在本项目范围之内。

默认语义约定

它默认使用 org.springframework.http.server.observation.DefaultServerRequestObservationConvention,由 ServerRequestObservationContext 支持。

默认情况下,会创建以下 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。

OpenTelemetry 语义约定

OpenTelemetry 变体可用于 org.springframework.http.server.observation.OpenTelemetryServerRequestObservationConvention,由 ServerRequestObservationContext 支持。

响应式应用程序

应用程序需要使用 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" 命名,用于阻塞和响应式客户端。此观测测量整个 HTTP 请求/响应交换,从建立连接到正文反序列化。与服务器端不同,检测直接在客户端实现,因此唯一需要的步骤是在客户端上配置 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.Builder 上配置 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 调用提供观测,因为它们没有适合此类检测的语义。默认情况下,事件发布和处理是同步且在同一线程上完成的。这意味着在任务执行期间,ThreadLocal 和日志上下文将与事件发布者相同。

如果应用程序全局配置了一个自定义的 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");
	}

}
© . This site is unofficial and not affiliated with VMware.