SpringBoot?異步線程間傳遞上下文方式
異步線程間傳遞上下文
需求
SpringBoot項目中,經(jīng)常使用@Async來開啟一個子線程來完成異步操作。主線程中的用戶信息需要傳遞給子線程
實現(xiàn)
啟用異步功能
在啟動類里加上@EnableAsync注解
@EnableAsync @SpringBootApplication public class Application {}
配置異步
新建一個配置類,實現(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ù)裝飾器
新建一個異步任務(wù)裝飾器,實現(xiàn)TaskDecorator接口,并重寫decorate方法
public class MyContextDecorator implements TaskDecorator { @Override @Nonnull public Runnable decorate(@Nonnull Runnable runnable) { // 獲取主線程中的請求信息(我們的用戶信息也放在里面) RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); return () -> { try { // 將主線程的請求信息,設(shè)置到子線程中 RequestContextHolder.setRequestAttributes(attributes); // 執(zhí)行子線程,這一步不要忘了 runnable.run(); } finally { // 線程結(jié)束,清空這些信息,否則可能造成內(nèi)存泄漏 RequestContextHolder.resetRequestAttributes(); } }; }
補充下:RequestContextHolder內(nèi)部是基于ThreadLocal實現(xiàn)的,因此在使用set get時,都是和當前線程綁定的。當然,使用者的用戶信息不一定放在了RequestContextHolder里面,讀者可以自行擴展。
到此,通過@Async開啟的子線程,就可以正常拿到父線程中的Request信息了。
啟用多線程安全上下文無法在線程間共享問題
問題
項目中多線程添加數(shù)據(jù),mybatisplus元數(shù)據(jù)填充功能,填充創(chuàng)建人時,數(shù)據(jù)是來自 spring security SecurityContextHolder.getContext.getAuthentication,同步操作時,能正常獲取,而異步執(zhí)行時空指針異常。
解決方案
配置安全上下文全局策略SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL)
原理
Spring Security 安全上下文默認策略為MODE_THREADLOCAL,ThreadLocal機制來保存每個使用者的安全上下文。
這意味著,只要針對某個使用者的邏輯執(zhí)行都是在同一個線程中進行,即使不在各個方法之間以參數(shù)的形式傳遞其安全上下文,各個方法也能通過SecurityContextHolder工具獲取到該安全上下文。
只要在處理完當前使用者的請求之后注意清除ThreadLocal中的安全上下文,這種使用ThreadLocal的方式是很安全的。
MODE_GLOBAL
: JVM中所有的線程使用同一個安全上下文MODE_INHERITABLETHREADLOCAL
:有些應(yīng)用會有自己的線程創(chuàng)建,并且希望這些新建線程也能使用創(chuàng)建者的安全上下文。這種效果,可以通過將SecurityContextHolder配置成MODE_INHERITABLETHREADLOCAL策略達到。
結(jié)果
在配置文件中添加:
@PostConstruct public void init(){ SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL); }
@PostConstruct注解好多人以為是Spring提供的。其實是Java自己的注解。
Java中該注解的說明:@PostConstruct該注解被用來修飾一個非靜態(tài)的void()方法。被@PostConstruct修飾的方法會在服務(wù)器加載Servlet的時候運行,并且只會被服務(wù)器執(zhí)行一次。PostConstruct在構(gòu)造函數(shù)之后執(zhí)行,init()方法之前執(zhí)行。
通常我們會是在Spring框架中使用到@PostConstruct注解 該注解的方法在整個Bean初始化中的執(zhí)行順序:
Constructor(構(gòu)造方法) -> @Autowired(依賴注入) -> @PostConstruct(注釋的方法)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
- 在 Spring Boot 中使用異步線程時的 HttpServletRequest 復(fù)用問題記錄
- SpringBoot異步線程父子線程數(shù)據(jù)傳遞的5種方式
- Spring?Boot異步線程間數(shù)據(jù)傳遞的四種方式
- springboot?正確的在異步線程中使用request的示例代碼
- SpringBoot獲取HttpServletRequest的3種方式總結(jié)
- SpringBoot詳細講解異步任務(wù)如何獲取HttpServletRequest
- SpringBoot實現(xiàn)任意位置獲取HttpServletRequest對象
- Spring Boot 中正確地在異步線程中使用 HttpServletRequest的方法
相關(guān)文章
Java實現(xiàn)注冊登錄與郵箱發(fā)送賬號驗證激活功能
這篇文章主要介紹了Java實現(xiàn)注冊登錄與郵箱發(fā)送賬號驗證激活功能,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2022-12-12java中關(guān)于文本文件的讀寫方法實例總結(jié)
這篇文章主要介紹了java中關(guān)于文本文件的讀寫方法,實例總結(jié)了Java針對文本文件讀寫的幾種常用方法,并對比了各個方法的優(yōu)劣及特點,具有一定參考借鑒價值,需要的朋友可以參考下2015-11-11詳解json在SpringBoot中的格式轉(zhuǎn)換
這篇文章主要介紹了詳解json在SpringBoot中的格式轉(zhuǎn)換,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11spring定時器@Scheduled異步調(diào)用方式
在Spring Boot中,@Schedule默認使用單線程執(zhí)行定時任務(wù),多個定時器會按順序執(zhí)行,為實現(xiàn)異步執(zhí)行,可以通過自定義線程池或?qū)崿F(xiàn)SchedulingConfigurer接口,使用自定義線程池可以保證多個定時器并發(fā)執(zhí)行2024-11-11關(guān)于Spring MVC在Controller層中注入request的坑詳解
這篇文章主要給大家介紹了關(guān)于Spring MVC在Controller層中注入request的坑的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2018-04-04