解決Spring在Thread中注入Bean無效的問題
在Thread中注入Bean無效
在Spring項目中,有時需要新開線程完成一些復(fù)雜任務(wù),而線程中可能需要注入一些服務(wù)。而通過Spring注入來管理和使用服務(wù)是較為合理的方式。但是若直接在Thread子類中通過注解方式注入Bean是無效的。
因為Spring本身默認Bean為單例模式構(gòu)建,同時是非線程安全的,因此禁止了在Thread子類中的注入行為,因此在Thread中直接注入的bean是null的,會發(fā)生空指針錯誤。
以下分別列舉錯誤的注入方法和兩種解決方式。
錯誤的注入方法
@Controller public class SomeController{ ? ? @ResponseBody ? ? @RequestMapping("test") ? ? String testInjection(){ ? ? ? ? // 直接創(chuàng)建并運行線程 ? ? ? ? new SomeThread().start(); ? ? } } // 直接編寫線程 public SomeThread extends Thread { ? ? @Autowired ? ? SomeService someService; ? ? @Override ? ? public void run(){ ? ? ? ? // do something... ? ? ? ? someService.doSomething(); ? ? ? ? // 此時 someService實例是null. ? ? } }
報NullpointException。
通過封裝Thread子類注入
個人比較推薦這種方法,對外部代碼的影響較小。
@Controller public class SomeController{ ? ? // 通過注解注入封裝線程的Bean ? ? @AutoWired ? ? SomeThread someThread; ? ? @ResponseBody ? ? @RequestMapping("test") ? ? String testInjection(){ ? ? ? ? // 通過注入的Bean啟動線程 ? ? ? ? someThread.execute(); ? ? } } @Component public class SomeThread { ? ? // 封裝Bean中注入服務(wù) ? ? @AutoWired ? ? SomeService someService ? ? public void execute() { ? ? ? ? new Worker().start(); ? ? } ? ? // 線程內(nèi)部類,Thread或者Runnable均可 ? ? private class Worker extends Thread { ? ? ? ? @Override ? ? ? ? public void run() { ? ? ? ? ? ? // do something... ? ? ? ? ? ? SomeThread.this.someService.doSomething(); ? ? ? ? ? ? // 此時someService已被注入,非null. ? ? ? ? } ? ? } }
正常調(diào)用someService。
通過外部引入
即在可以注入的地方先得到可用的實例,在通過Thread子類的構(gòu)造函數(shù)引入。這樣會使得在進行代碼修改時,影響到每個使用Thread子類的代碼,修改工作量大。
@Controller public class SomeController{ ? ? // 通過注解注入Service ? ? @AutoWired ? ? SomeService someService; ? ? @ResponseBody ? ? @RequestMapping("test") ? ? String testInjection(){ ? ? ? ? // 通過構(gòu)造函數(shù)從外部引入 ? ? ? ? new Worker(someService).start(); ? ? } } public class SomeThread { ? ? private SomeService someService; ? ? public SomeThread(SomeService someService){ ? ? ? ? // 通過構(gòu)造函數(shù)從外部引入 ? ? ? ? this.someService ?= someService; ? ? } ? ? @Override ? ? public void run() { ? ? ? ? // do something... ? ? ? ? someService.doSomething(); ? ? ? ? // 此時someService非null. ? ? } }
Spring多線程中bean的注入問題
最近碰到了一個問題,使用SSM框架,在Service層需要另開一個線程,這個線程專門用來做一些操作,并將結(jié)果寫入數(shù)據(jù)庫中。但是在線程中使用@Resource或者@Autowired注入全部為NULL,原來是Spring不能在線程中注入。
網(wǎng)上的主要解決方法有
- 將需要的Bean作為線程的的構(gòu)造函數(shù)的參數(shù)傳入
- 使用ApplicationContext.getBean方法來靜態(tài)的獲取Bean
我的線程中所需要的Bean的數(shù)量較多,并且以后還有可能增加或者減少,所以方法1并不適合
我的Spring配置文件并不只一個,而且使用getBean方法需要重新加載一遍所有的Bean,這樣也違反的Spring的IoC,并不是我想要的,所以也不采用方法2
最后確定使用內(nèi)部類的方法,將線程中需要的Bean提前注入好,大致的結(jié)構(gòu)如下:
@Service class TestExample{ //這兩個為線程所需要的Bean @Resource TestDao testDao; @Resource NeedDap needDao; public void serviceExecute(){ //在這里開啟線程,執(zhí)行操作 ThreadExample te = new ThreadExample(); te.start(); } //內(nèi)部類 private class ThreadExample extends Thread{ public ThreadExample(){ //也可以在構(gòu)造函數(shù)中傳入?yún)?shù) } public void run(){ //這里為線程的操作 //就可以使用注入之后Bean了 } } }
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Mybatis?MappedStatement類核心原理詳解
這篇文章主要介紹了Mybatis?MappedStatement類,mybatis的mapper文件最終會被解析器,解析成MappedStatement,其中insert|update|delete|select每一個標簽分別對應(yīng)一個MappedStatement2022-11-11使用spring實現(xiàn)郵件的發(fā)送實例(含測試,源碼,注釋)
本篇文章主要介紹了使用spring實現(xiàn)郵件的發(fā)送實例,詳細的介紹了使用spring配置實現(xiàn)郵件發(fā)送,含測試,源碼,注釋,有興趣的可以下2017-05-05詳解MyBatisPlus如何實現(xiàn)分頁和查詢操作
這篇文章主要為大家詳細介紹了MyBatisPlus是如何實現(xiàn)分頁和查詢操作的,文中的示例代碼講解詳細,對我們學習有一定的幫助,需要的可以參考一下2022-05-05微服務(wù)Redis-Session共享登錄狀態(tài)的過程詳解
這篇文章主要介紹了微服務(wù)Redis-Session共享登錄狀態(tài),本文采取Spring security做登錄校驗,用redis做session共享,實現(xiàn)單服務(wù)登錄可靠性,微服務(wù)之間調(diào)用的可靠性與通用性,需要的朋友可以參考下2023-12-12Eclipse/MyEclipse轉(zhuǎn)IntelliJ IDEA完全攻略(圖文)
這篇文章主要介紹了Eclipse/MyEclipse轉(zhuǎn)IntelliJ IDEA完全攻略(圖文),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-01-01