日志框架系列講解文章
日志框架 - 基于spring-boot - 使用入門
日志框架 - 基于spring-boot - 設(shè)計(jì)
日志框架 - 基于spring-boot - 實(shí)現(xiàn)1 - 配置文件
日志框架 - 基于spring-boot - 實(shí)現(xiàn)2 - 消息定義及消息日志打印
日志框架 - 基于spring-boot - 實(shí)現(xiàn)3 - 關(guān)鍵字與三種消息解析器
日志框架 - 基于spring-boot - 實(shí)現(xiàn)4 - HTTP請(qǐng)求攔截
日志框架 - 基于spring-boot - 實(shí)現(xiàn)5 - 線程切換
日志框架 - 基于spring-boot - 實(shí)現(xiàn)6 - 自動(dòng)裝配
上一篇我們講了框架實(shí)現(xiàn)的第四部分:實(shí)現(xiàn)HTTP請(qǐng)求的攔截
本篇主要講框架實(shí)現(xiàn)的第五部分:如何在線程切換時(shí)保留上下文信息。
由于 Logback 的 MDC 實(shí)際上是一個(gè) ThreadLocal 的實(shí)現(xiàn)(參考這里)奏黑,因此炊邦,當(dāng)異步執(zhí)行產(chǎn)生線程切換時(shí),需要將 MDC 保存的信息進(jìn)行切換攀涵。
為了實(shí)現(xiàn)此功能铣耘,我研究了 Spring 的異步執(zhí)行組件洽沟,發(fā)現(xiàn) Spring 中有一個(gè)可用的線程裝飾器TaskDecorator以故。這個(gè)是Spring Core 4.3版本才加入的接口,在Spring的文檔中沒(méi)有提及裆操,也沒(méi)有提供任何實(shí)現(xiàn)怒详,但正好是我所需要的功能炉媒。代碼如下。
/**
* 解決異步執(zhí)行時(shí)MDC內(nèi)容延續(xù)的問(wèn)題
*/
public class MDCTaskDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
return new MDCContinueRunableDecorator(runnable);
}
/**
* 執(zhí)行線程裝飾器
*/
protected class MDCContinueRunableDecorator implements Runnable {
private final Runnable delegate;
protected final Map<String, String> logContextMap;
public MDCContinueRunableDecorator(Runnable runnable) {
this.delegate = runnable;
this.logContextMap = MDC.getCopyOfContextMap();
}
@Override
public void run() {
MDC.setContextMap(this.logContextMap);
this.delegate.run();
MDC.clear();
}
}
}
然后昆烁,需要自定義實(shí)現(xiàn)一個(gè) TaskExecutor吊骤,替換Spring提供的默認(rèn)實(shí)現(xiàn),代碼如下静尼。
/**
* 自定義線程池
* <p>
* 用于線程切換時(shí)的MDC延續(xù)
*/
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(maxPoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setTaskDecorator(new MDCTaskDecorator());
executor.setThreadNamePrefix("MDCAdaptTaskExcutor-");
executor.initialize();
return executor;
}
至此白粉,線程切換時(shí)的問(wèn)題已經(jīng)解決。只要異步處理使用了自定義的 TaskExecutor 鼠渺,即可實(shí)現(xiàn)上下文的自動(dòng)傳遞鸭巴。