溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

SpringBoot?異步線程間傳遞上下文方式是怎樣的

發(fā)布時間:2021-11-24 13:15:02 來源:億速云 閱讀:316 作者:柒染 欄目:開發(fā)技術(shù)

這篇文章給大家介紹SpringBoot 異步線程間傳遞上下文方式是怎樣的,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

    異步線程間傳遞上下文

    需求

    SpringBoot項目中,經(jīng)常使用@Async來開啟一個子線程來完成異步操作。主線程中的用戶信息需要傳遞給子線程

    實(shí)現(xiàn)

    啟用異步功能

    在啟動類里加上@EnableAsync注解

    @EnableAsync
    @SpringBootApplication
    public class Application {}
    配置異步

    新建一個配置類,實(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ù)裝飾器

    新建一個異步任務(wù)裝飾器,實(shí)現(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();
                }
            };
        }

    補(bǔ)充下:RequestContextHolder內(nèi)部是基于ThreadLocal實(shí)現(xiàn)的,因此在使用set get時,都是和當(dāng)前線程綁定的。當(dāng)然,使用者的用戶信息不一定放在了RequestContextHolder里面,讀者可以自行擴(kuò)展。

    到此,通過@Async開啟的子線程,就可以正常拿到父線程中的Request信息了。

    啟用多線程安全上下文無法在線程間共享問題

    問題

    項目中多線程添加數(shù)據(jù),mybatisplus元數(shù)據(jù)填充功能,填充創(chuàng)建人時,數(shù)據(jù)是來自 spring security SecurityContextHolder.getContext.getAuthentication,同步操作時,能正常獲取,而異步執(zhí)行時空指針異常。

    解決方案

    配置安全上下文全局策略SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL)

    原理

    Spring Security 安全上下文默認(rèn)策略為MODE_THREADLOCAL,ThreadLocal機(jī)制來保存每個使用者的安全上下文。

    這意味著,只要針對某個使用者的邏輯執(zhí)行都是在同一個線程中進(jìn)行,即使不在各個方法之間以參數(shù)的形式傳遞其安全上下文,各個方法也能通過SecurityContextHolder工具獲取到該安全上下文。

    只要在處理完當(dāng)前使用者的請求之后注意清除ThreadLocal中的安全上下文,這種使用ThreadLocal的方式是很安全的。

    • MODE_GLOBAL: JVM中所有的線程使用同一個安全上下文

    • MODE_INHERITABLETHREADLOCAL:有些應(yīng)用會有自己的線程創(chuàng)建,并且希望這些新建線程也能使用創(chuàng)建者的安全上下文。這種效果,可以通過將SecurityContextHolder配置成MODE_INHERITABLETHREADLOCAL策略達(dá)到。

    結(jié)果

    在配置文件中添加:

    @PostConstruct
    public void init(){
        SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
    }

    @PostConstruct注解好多人以為是Spring提供的。其實(shí)是Java自己的注解。

    Java中該注解的說明:@PostConstruct該注解被用來修飾一個非靜態(tài)的void()方法。被@PostConstruct修飾的方法會在服務(wù)器加載Servlet的時候運(yùn)行,并且只會被服務(wù)器執(zhí)行一次。PostConstruct在構(gòu)造函數(shù)之后執(zhí)行,init()方法之前執(zhí)行。

    通常我們會是在Spring框架中使用到@PostConstruct注解 該注解的方法在整個Bean初始化中的執(zhí)行順序:

    Constructor(構(gòu)造方法) -> @Autowired(依賴注入) -> @PostConstruct(注釋的方法)

    關(guān)于SpringBoot 異步線程間傳遞上下文方式是怎樣的就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI