SpringBoot 1.3.8.RELEASE:无需TaskDecorator实现MDC跨线程传递
解决Spring Boot 1.3.x中@Async的MDC上下文跨线程传递问题
由于你的项目无法升级Spring Core(4.2.8.RELEASE)或降级Logback,无法使用TaskDecorator,以下是三种可行的替代方案:
方案一:自定义AsyncConfigurer包装线程池任务
通过实现AsyncConfigurer接口,自定义线程池并包装任务提交逻辑,手动传递MDC上下文:
1. 编写MDC任务包装类
import org.slf4j.MDC; import java.util.Map; import java.util.concurrent.Callable; // 包装Runnable,传递MDC上下文 public class MdcRunnable implements Runnable { private final Runnable delegate; private final Map<String, String> mdcContext; public MdcRunnable(Runnable delegate) { this.delegate = delegate; this.mdcContext = MDC.getCopyOfContextMap(); } @Override public void run() { Map<String, String> originalContext = MDC.getCopyOfContextMap(); try { if (mdcContext != null) { MDC.setContextMap(mdcContext); } delegate.run(); } finally { if (originalContext != null) { MDC.setContextMap(originalContext); } else { MDC.clear(); } } } } // 包装Callable,传递MDC上下文 public class MdcCallable<V> implements Callable<V> { private final Callable<V> delegate; private final Map<String, String> mdcContext; public MdcCallable(Callable<V> delegate) { this.delegate = delegate; this.mdcContext = MDC.getCopyOfContextMap(); } @Override public V call() throws Exception { Map<String, String> originalContext = MDC.getCopyOfContextMap(); try { if (mdcContext != null) { MDC.setContextMap(mdcContext); } return delegate.call(); } finally { if (originalContext != null) { MDC.setContextMap(originalContext); } else { MDC.clear(); } } } }
2. 自定义AsyncConfigurer配置线程池
import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.Executor; import java.util.concurrent.Future; @Configuration public class CustomAsyncConfigurer implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(25); executor.setThreadNamePrefix("Async-"); executor.initialize(); // 包装线程池的任务提交逻辑 return new Executor() { @Override public void execute(Runnable command) { executor.execute(new MdcRunnable(command)); } @Override public <T> Future<T> submit(Callable<T> task) { return executor.submit(new MdcCallable<>(task)); } @Override public <T> Future<T> submit(Runnable task, T result) { return executor.submit(new MdcRunnable(task), result); } @Override public Future<?> submit(Runnable task) { return executor.submit(new MdcRunnable(task)); } }; } }
该方案会自动为所有@Async任务传递MDC上下文,执行完成后恢复子线程原有上下文,避免污染。
方案二:Spring AOP拦截@Async方法统一处理
通过AOP拦截所有@Async标注的方法,在子线程执行前注入主线程MDC上下文:
1. 确保引入AOP依赖(Spring Boot默认已包含)
如果项目未引入,添加以下依赖到pom.xml:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
2. 编写MDC切面类
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.MDC; import org.springframework.stereotype.Component; import java.util.Map; @Aspect @Component public class AsyncMdcAspect { @Pointcut("@annotation(org.springframework.scheduling.annotation.Async)") public void asyncMethodPointcut() {} @Around("asyncMethodPointcut()") public Object wrapAsyncMethod(ProceedingJoinPoint joinPoint) throws Throwable { Map<String, String> mdcContext = MDC.getCopyOfContextMap(); // 用Callable包裹方法调用,传递MDC上下文 return ((Callable<Object>) () -> { Map<String, String> originalContext = MDC.getCopyOfContextMap(); try { if (mdcContext != null) { MDC.setContextMap(mdcContext); } return joinPoint.proceed(); } finally { if (originalContext != null) { MDC.setContextMap(originalContext); } else { MDC.clear(); } } }).call(); } }
该方案无需修改线程池或异步方法,通过AOP实现统一拦截,侵入性低。
方案三:手动传递MDC上下文(适合少量异步场景)
如果项目中异步方法数量较少,可直接在调用时手动传递MDC上下文:
主线程调用异步方法
import org.slf4j.MDC; import java.util.Map; // 捕获主线程MDC上下文 Map<String, String> mdcContext = MDC.getCopyOfContextMap(); // 传入异步方法 asyncService.processAsyncTask(mdcContext);
异步方法处理上下文
import org.slf4j.MDC; import java.util.Map; @Async public void processAsyncTask(Map<String, String> mdcContext) { Map<String, String> originalContext = MDC.getCopyOfContextMap(); try { if (mdcContext != null) { MDC.setContextMap(mdcContext); } // 业务逻辑执行 logger.info("异步任务执行,MDC上下文已传递"); } finally { // 恢复子线程原有上下文 if (originalContext != null) { MDC.setContextMap(originalContext); } else { MDC.clear(); } } }
该方案直观但侵入性较强,适合异步方法较少的场景。
内容的提问来源于stack exchange,提问作者Alfred Souza
相关产品推荐
相关产品推荐

