可觀測性支援

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.