解決Spring在Thread中註入Bean無效的問題
在Thread中註入Bean無效
在Spring項目中,有時需要新開線程完成一些復雜任務,而線程中可能需要註入一些服務。而通過Spring註入來管理和使用服務是較為合理的方式。但是若直接在Thread子類中通過註解方式註入Bean是無效的。
因為Spring本身默認Bean為單例模式構建,同時是非線程安全的,因此禁止瞭在Thread子類中的註入行為,因此在Thread中直接註入的bean是null的,會發生空指針錯誤。
以下分別列舉錯誤的註入方法和兩種解決方式。
錯誤的註入方法
@Controller public class SomeController{ @ResponseBody @RequestMapping("test") String testInjection(){ // 直接創建並運行線程 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中註入服務 @AutoWired SomeService someService public void execute() { new Worker().start(); } // 線程內部類,Thread或者Runnable均可 private class Worker extends Thread { @Override public void run() { // do something... SomeThread.this.someService.doSomething(); // 此時someService已被註入,非null. } } }
正常調用someService。
通過外部引入
即在可以註入的地方先得到可用的實例,在通過Thread子類的構造函數引入。這樣會使得在進行代碼修改時,影響到每個使用Thread子類的代碼,修改工作量大。
@Controller public class SomeController{ // 通過註解註入Service @AutoWired SomeService someService; @ResponseBody @RequestMapping("test") String testInjection(){ // 通過構造函數從外部引入 new Worker(someService).start(); } } public class SomeThread { private SomeService someService; public SomeThread(SomeService someService){ // 通過構造函數從外部引入 this.someService = someService; } @Override public void run() { // do something... someService.doSomething(); // 此時someService非null. } }
Spring多線程中,bean的註入問題
最近碰到瞭一個問題,使用SSM框架,在Service層需要另開一個線程,這個線程專門用來做一些操作,並將結果寫入數據庫中。但是在線程中使用@Resource或者@Autowired註入全部為NULL,原來是Spring不能在線程中註入。
網上的主要解決方法有
- 將需要的Bean作為線程的的構造函數的參數傳入
- 使用ApplicationContext.getBean方法來靜態的獲取Bean
我的線程中所需要的Bean的數量較多,並且以後還有可能增加或者減少,所以方法1並不適合
我的Spring配置文件並不隻一個,而且使用getBean方法需要重新加載一遍所有的Bean,這樣也違反的Spring的IoC,並不是我想要的,所以也不采用方法2
最後確定使用內部類的方法,將線程中需要的Bean提前註入好,大致的結構如下:
@Service class TestExample{ //這兩個為線程所需要的Bean @Resource TestDao testDao; @Resource NeedDap needDao; public void serviceExecute(){ //在這裡開啟線程,執行操作 ThreadExample te = new ThreadExample(); te.start(); } //內部類 private class ThreadExample extends Thread{ public ThreadExample(){ //也可以在構造函數中傳入參數 } public void run(){ //這裡為線程的操作 //就可以使用註入之後Bean瞭 } } }
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- Spring在多線程下@Resource註入為null的問題
- 深入分析@Resource和@Autowired註解區別
- 詳解Spring bean的註解註入之@Autowired的原理及使用
- Spring中的註解之@Override和@Autowired
- 淺談spring DI 依賴註入方式和區別