PAYATHON 2026

Spring Boot 3 中虚拟线程如何传播 MDC 和 SecurityContext?

数据阿明

结论

虚拟线程不会自动继承请求线程中的普通 ThreadLocal。要传播上下文,可以在异步执行器提交任务时捕获上下文,并在任务运行时恢复。对于 CompletableFuture 使用的原生 ExecutorService,可以用 Micrometer Context Propagation 的 ContextExecutorService 包装虚拟线程执行器。这样每个任务仍会创建一个虚拟线程。

这种方式只对通过该执行器提交的任务生效。Spring 的 TaskDecorator 只作用于配置了它的 Spring TaskExecutor,不会自动拦截任意 ExecutorService,也不会覆盖应用创建的所有虚拟线程。Spring Boot 文档也将上下文传播描述为异步执行器的配置,而非 JVM 范围内的自动机制。Spring Boot:Context Propagation;Micrometer:Context Propagation 的用途

配置虚拟线程执行器

让异步调用共用一个经过包装的执行器:

import io.micrometer.context.ContextExecutorService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Configuration
class AsyncConfiguration {

    @Bean(destroyMethod = "close")
    ExecutorService virtualThreadExecutor() {
        return ContextExecutorService.wrap(
                Executors.newVirtualThreadPerTaskExecutor());
    }
}

原有的 CompletableFuture 调用可以继续使用这个 bean:

CompletableFuture.runAsync(() -> {
    String traceId = MDC.get("traceId");
    var authentication = SecurityContextHolder.getContext().getAuthentication();
    String user = authentication != null ? authentication.getName() : "NULL";

    log.info("Virtual Thread - traceId: {}, user: {}", traceId, user);
}, virtualThreadExecutor);

ContextExecutorService 会在提交任务时捕获已注册的上下文访问器提供的值,并在任务运行时恢复这些值。包装执行器本身不会提供 MDC 或 SecurityContext 的访问器,因此需要确认项目依赖中包含相应实现,并且这些实现已注册。Micrometer Context Propagation 使用 ThreadLocalAccessor 机制;Spring Security 从 6.5 起提供 SecurityContextHolderThreadLocalAccessor,该库位于 classpath 时可通过 ServiceLoader 注册。Micrometer:跨线程传播;Spring Security:SecurityContextHolderThreadLocalAccessor

@Async 的配置边界

@Async 也需要实际使用包装后的执行器。可以通过 AsyncConfigurer#getAsyncExecutor() 返回该 bean,也可以将它配置为 Spring 异步调用所选用的执行器。仅声明一个 ContextPropagatingTaskDecorator bean,不会让手动创建的 Executors.newVirtualThreadPerTaskExecutor() 自动经过装饰;TaskDecorator 只对接入它的 Spring TaskExecutor 生效。Spring Framework:ContextPropagatingTaskDecorator

版本与安全注意事项

Spring Boot 3.x 涵盖多个 Spring Security 和 Micrometer 版本。上述 SecurityContextHolderThreadLocalAccessor 从 Spring Security 6.5 起提供。较早版本如果没有这个访问器,需要为 SecurityContextHolder 注册兼容的 ThreadLocalAccessor,或使用 Spring Security 的 DelegatingSecurityContextExecutorService 专门传播安全上下文。具体做法取决于项目使用的依赖版本。MDC 也需要有可用的访问器,单独引入 ContextExecutorService 并不能保证 MDC 会传播。

不要把 SecurityContextHolder.MODE_INHERITABLETHREADLOCAL 当作通用方案。它会改变全局上下文策略,而且异步任务可能在请求结束后才运行。应确保上下文只在任务执行期间恢复,并在任务结束时清理。Spring Security 默认使用线程本地上下文,并由安全过滤器链负责清理请求线程。Spring Security:认证架构

备注:内容仅供参考。