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