SpringBoot?異步線程間傳遞上下文方式
異步線程間傳遞上下文
需求
SpringBoot項(xiàng)目中,經(jīng)常使用@Async來(lái)開啟一個(gè)子線程來(lái)完成異步操作。主線程中的用戶信息需要傳遞給子線程
實(shí)現(xiàn)
啟用異步功能
在啟動(dòng)類里加上@EnableAsync注解
@EnableAsync
@SpringBootApplication
public class Application {}
配置異步
新建一個(gè)配置類,實(shí)現(xiàn)AsyncConfigurer接口,并重寫getAsyncExecutor方法
@Configuration
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(50);
executor.setThreadNamePrefix("async-pool-");
// 這一步是關(guān)鍵,異步Task裝飾器
executor.setTaskDecorator(new MyContextDecorator());
executor.initialize();
return executor;
}
}
配置任務(wù)裝飾器
新建一個(gè)異步任務(wù)裝飾器,實(shí)現(xiàn)TaskDecorator接口,并重寫decorate方法
public class MyContextDecorator implements TaskDecorator {
@Override
@Nonnull
public Runnable decorate(@Nonnull Runnable runnable) {
// 獲取主線程中的請(qǐng)求信息(我們的用戶信息也放在里面)
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
return () -> {
try {
// 將主線程的請(qǐng)求信息,設(shè)置到子線程中
RequestContextHolder.setRequestAttributes(attributes);
// 執(zhí)行子線程,這一步不要忘了
runnable.run();
} finally {
// 線程結(jié)束,清空這些信息,否則可能造成內(nèi)存泄漏
RequestContextHolder.resetRequestAttributes();
}
};
}
補(bǔ)充下:RequestContextHolder內(nèi)部是基于ThreadLocal實(shí)現(xiàn)的,因此在使用set get時(shí),都是和當(dāng)前線程綁定的。當(dāng)然,使用者的用戶信息不一定放在了RequestContextHolder里面,讀者可以自行擴(kuò)展。
到此,通過(guò)@Async開啟的子線程,就可以正常拿到父線程中的Request信息了。
啟用多線程安全上下文無(wú)法在線程間共享問(wèn)題
問(wèn)題
項(xiàng)目中多線程添加數(shù)據(jù),mybatisplus元數(shù)據(jù)填充功能,填充創(chuàng)建人時(shí),數(shù)據(jù)是來(lái)自 spring security SecurityContextHolder.getContext.getAuthentication,同步操作時(shí),能正常獲取,而異步執(zhí)行時(shí)空指針異常。
解決方案
配置安全上下文全局策略SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL)
原理
Spring Security 安全上下文默認(rèn)策略為MODE_THREADLOCAL,ThreadLocal機(jī)制來(lái)保存每個(gè)使用者的安全上下文。
這意味著,只要針對(duì)某個(gè)使用者的邏輯執(zhí)行都是在同一個(gè)線程中進(jìn)行,即使不在各個(gè)方法之間以參數(shù)的形式傳遞其安全上下文,各個(gè)方法也能通過(guò)SecurityContextHolder工具獲取到該安全上下文。
只要在處理完當(dāng)前使用者的請(qǐng)求之后注意清除ThreadLocal中的安全上下文,這種使用ThreadLocal的方式是很安全的。
MODE_GLOBAL: JVM中所有的線程使用同一個(gè)安全上下文MODE_INHERITABLETHREADLOCAL:有些應(yīng)用會(huì)有自己的線程創(chuàng)建,并且希望這些新建線程也能使用創(chuàng)建者的安全上下文。這種效果,可以通過(guò)將SecurityContextHolder配置成MODE_INHERITABLETHREADLOCAL策略達(dá)到。
結(jié)果
在配置文件中添加:
@PostConstruct
public void init(){
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
}
@PostConstruct注解好多人以為是Spring提供的。其實(shí)是Java自己的注解。
Java中該注解的說(shuō)明:@PostConstruct該注解被用來(lái)修飾一個(gè)非靜態(tài)的void()方法。被@PostConstruct修飾的方法會(huì)在服務(wù)器加載Servlet的時(shí)候運(yùn)行,并且只會(huì)被服務(wù)器執(zhí)行一次。PostConstruct在構(gòu)造函數(shù)之后執(zhí)行,init()方法之前執(zhí)行。
通常我們會(huì)是在Spring框架中使用到@PostConstruct注解 該注解的方法在整個(gè)Bean初始化中的執(zhí)行順序:
Constructor(構(gòu)造方法) -> @Autowired(依賴注入) -> @PostConstruct(注釋的方法)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- 在 Spring Boot 中使用異步線程時(shí)的 HttpServletRequest 復(fù)用問(wèn)題記錄
- SpringBoot異步線程父子線程數(shù)據(jù)傳遞的5種方式
- Spring?Boot異步線程間數(shù)據(jù)傳遞的四種方式
- springboot?正確的在異步線程中使用request的示例代碼
- SpringBoot獲取HttpServletRequest的3種方式總結(jié)
- SpringBoot詳細(xì)講解異步任務(wù)如何獲取HttpServletRequest
- SpringBoot實(shí)現(xiàn)任意位置獲取HttpServletRequest對(duì)象
- Spring?Boot?中正確地在異步線程中使用?HttpServletRequest的方法
相關(guān)文章
詳解java.lang.NumberFormatException錯(cuò)誤及解決辦法
這篇文章主要介紹了詳解java.lang.NumberFormatException錯(cuò)誤及解決辦法,本文詳解的介紹了錯(cuò)誤的解決方法,感興趣的可以一起來(lái)了解一下2020-05-05
Java 異常的棧軌跡(Stack Trace)詳解及實(shí)例代碼
這篇文章主要介紹了Java 異常的棧軌跡(Stack Trace)詳解及實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-03-03
SpringBoot詳解如何進(jìn)行整合Druid數(shù)據(jù)源
Druid是阿里開發(fā)的一款開源的數(shù)據(jù)源,被很多人認(rèn)為是Java語(yǔ)言中最好的數(shù)據(jù)庫(kù)連接池,本文主要介紹了SpringBoot整合Druid數(shù)據(jù)源的方法實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06
SpringMVC開發(fā)restful API之用戶查詢代碼詳解
這篇文章主要介紹了SpringMVC開發(fā)restful API之用戶查詢代碼詳解,小編覺(jué)得挺不錯(cuò)的,這里分享給大家,需要的朋友可以參考。下面隨小編一起看看吧。2017-11-11

