跳到主要内容
版本:7.0.3

可观测性支持

Hunyuan 7b 中英对照 Observability Support

Micrometer定义了一个[观测概念(Observation concept)],该概念在应用程序中既支持指标(Metrics)也支持轨迹(Traces)。指标(Metrics)提供了一种创建计时器(timers)、计量器(gauges)或计数器(counters)的方法,用于收集关于应用程序运行时行为的统计数据。指标可以帮助你追踪错误率、使用模式、性能等信息。而轨迹(Traces)则能提供对整个系统的全面视图,能够跨越应用程序的边界;你可以聚焦于特定的用户请求,并跟踪其在整个应用程序中的完整执行过程。

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

产生的观测列表

Spring框架提供了多种用于可观测性的功能。如本节开头所概述的,根据配置的不同,观测结果可以生成定时指标(timer Metrics)和/或跟踪数据(Traces)。

表1. Spring框架生成的观察结果

观测名称描述
"http.client.requests"HTTP客户端交互所花费的时间
"http.server_requests"框架层面处理HTTP服务器交互所花费的时间
"jms.message.publish"消息生产者向目标发送JMS消息所花费的时间
"jms.message.process"消息消费者之前接收到的JMS消息的处理时间
"tasks.scheduled_execution"@Scheduled任务执行的处理时间
备注

观察指标(Observations)使用 Micrometer 的官方命名规范,但度量指标(Metrics)的名称将自动转换为监控系统后端所偏好的格式(如 Prometheus、Atlas、Graphite、InfluxDB 等)。

微米级观测概念

如果你不熟悉Micrometer Observation,以下是你应该了解的概念的简要概述。

  • Observation 是应用程序中实际发生事件的记录。这些记录由 ObservationHandler 的实现进行处理,以生成指标(metrics)或跟踪数据(traces)。

  • 每个观察记录(observation)都对应一个 ObservationContext 的实现;该类型包含提取元数据所需的所有相关信息。在 HTTP 服务器观察记录的情况下,ObservationContext 可以包含 HTTP 请求、HTTP 响应、处理过程中抛出的任何异常等。

  • 每个 Observation 都包含 KeyValues 元数据。对于 HTTP 服务器观察记录而言,这些元数据可能包括 HTTP 请求方法、HTTP 响应状态等。这些元数据由 ObservationConvention 的实现提供,这些实现需要声明它们所支持的 ObservationContext 类型。

  • 如果 KeyValue 元组(key-value pair)的可能值数量较少且有上限,则称这些值为“低基数”(low cardinality)(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());
}

}

如果你想要完全的控制权,你可以为你感兴趣的观察(observation)实现整个约定合同(convention contract):

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来实现,该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方法的名称。
code.namespace (必填)存储该调度方法的bean实例所属类的官方名称;对于匿名类,则使用“ANONYMOUS”。
error (必填)执行过程中抛出的异常的类名;如果没有抛出异常,则使用“none”。
exception (已弃用)error键重复,未来可能会被移除。
outcome (必填)方法执行的结果。可以是“SUCCESS”、“ERROR”或“UNKNOWN”(例如在执行过程中操作被取消)。

JMS消息传递监控

如果类路径中包含了io.micrometer:micrometer-jakarta9依赖,Spring Framework会使用Micrometer提供的Jakarta JMS instrumentation。io.micrometer.jakarta9.instrument.jms.JmsInstrumentation会对jakarta.jms.Session进行监控,并记录相关的观测数据。

这种仪器将产生两种类型的观测数据:

  • 当JMS消息被发送到代理时,使用“jms.message.publish”,通常是通过JmsTemplate来实现的。
  • 当JMS消息被应用程序处理时,使用“jms.message.process”,通常是通过MessageListener或带有@JmsListener注解的方法来处理的。
备注

目前对于“jms.message.receive”操作没有进行监控,因为测量等待消息接收所花费的时间几乎没有实际意义。通常情况下,这种监控会针对MessageConsumer#receive方法的调用进行跟踪。但是当这些方法执行完成后,处理时间就无法被测量到,且追踪范围也无法传递到应用程序中。

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

表3. 低基数键

名称描述
error在消息传递操作期间抛出的异常的类名(或“none”)。
exception (已弃用)error 键重复,未来可能会被移除。
messaging.destination.temporary (必需)目的地是 TemporaryQueue 还是 TemporaryTopic(值:“true”或“false”)。
messaging.operation (必需)正在执行的 JMS 操作的名称(值:“publish”或“process”)。

表4. 高基数键

名称描述
messaging.message.conversation_idJMS消息的相关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]注解的方法(jms/annotated.md#jms-annotated)来处理传入的消息。你需要确保在专用的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 服务器监控

对于Servlet和Reactive应用程序,HTTP服务器交换观测数据的名称为"http.server_requests";如果使用OpenTelemetry规范,则名称为"http.server.request.duration"

Servlet应用程序

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

只有当“异常”未被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 {
}

}
备注

由于仪器化(instrumentation)是在Servlet过滤器(Servlet Filter)层面进行的,因此观察范围仅涵盖在此过滤器之后按顺序排列的过滤器以及请求的处理过程。通常,Servlet容器的错误处理是在更低的层级进行的,不会涉及任何主动的观察(active observation)或时间跨度(span)记录。对于这种用例,需要特定的容器实现,例如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 语义约定

org.springframework.http.server.observation.OpenTelemetryServerRequestObservationConvention 提供了一种 OpenTelemetry 变体,该变体由 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未被应用程序的Controller处理时,该异常才会被记录为错误。通常,由Spring WebFlux的@ExceptionHandler以及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

表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客户端交换观测(observation)被命名为“http.client_requests”,适用于阻塞式(blocking)和响应式(reactive)客户端。该观测指标衡量整个HTTP请求/响应的交互过程,从建立连接一直到数据体的反序列化(deserialization)。与服务器端的对应机制不同,这种观测功能是直接在客户端实现的,因此唯一需要做的步骤就是在客户端上配置一个ObservationRegistry

RestTemplate

应用程序必须在RestTemplate实例上配置一个ObservationRegistry以启用监控功能;如果没有这个配置,那么监控数据将不起任何作用。Spring Boot会自动配置RestTemplateBuilder bean,并预先设置好观察注册表(observation registry)。

默认情况下,Instrumentation 使用 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以启用测量功能;如果没有这样做,观测数据将“不起作用”(即不会被记录或分析)。

默认情况下,Instrumentation 使用 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,并预先设置好观察注册表(observation registry)。

默认情况下,Instrumentation 使用的是 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框架并没有为[@EventListener调用]提供观察(Observations)功能(参见../core/beans/context-introduction.md#context-functionality-events-annotation),因为这些调用并不适合使用这种观测机制。默认情况下,事件的发布和处理是同步进行的,并且在同一线程上完成。这意味着在执行该任务期间,ThreadLocal和日志记录的上下文将与事件发布者相同。

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

}