在寫一個文件暫存系統的時候,有這樣的一個需求:一個定時任務,每隔t分鐘,做一些掃描操作。
實現思路是繼承ContextLoaderListener,實現自己的Listener,在這個Listener中,開啓定時任務。
一開始直接使用@Componet註解+@Autowired註解,結果導致注入失敗。
由於一開始沒意識到是線程的原因,所以在上網搜的原因及解決方案都是無效的,但也有一些有用的知識:
1.一般@Autowired注入失敗有以下一個原因:
1.包沒掃描到。
2.controller層的@controller或者service層的@Service註解沒寫
3.如果使用@Resource 注入service需要注意對象名字。
2.默認情況下,<context:component-scan>查找使用構造型(stereotype)註解所標註的類,如@Component(組件),@Service(服務),@Controller(控制器),@Repository(數據倉庫)。
3.Spring和SpringMVC整合的問題,二者在整合的時候,會產生兩個容器,一個Spring容器,一個SpringMVC容器。Spring容器是父容器,SpringMVC容器是Spring容器的子容器。Spring容器初始化後,SpringMVC再進行初始化,並將Spring容器作爲它的父容器。
子容器能夠訪問父容器的bean,而反之不行。
當父容器的初始化路徑(base-package)等於或者包括了子容器的路徑,那麼可能出現,當SpringMVC子容器初始化時,如果發現有相同的類,會將新初始化的實例覆蓋Spring容器中已經存在的實例,這可能導致注入失敗,空指針的問題。
所以要做到2個容器要掃描的包必須相互區分,互不干擾。Spring容器掃描非Controller的實例,而SpringMVC容器掃描Controller的實例就行。
然後在一個論壇中得知了是線程的原因:
“因爲主線程中可以直接使用@Autowired註解進行注入,但是在新開的線程中就不可以進行注入,每次啓動服務器就會報空指針異常,新開啓的線程直接就掛掉了,網上也搜了好多,原來是spring不會去管理這個新的線程,所以也就注入不進去任何bean。註解得方式最終都不能注入。”
有以下幾種方式來獲取,我使用的是:通過工具類獲取。Spring工具類獲取bean。
這種方法必須要把該工具類在SpringApplicationContext中註冊bean,這樣Spring才能通過工具類的set方法給其注入context。這樣也帶出了這種方法的弊端:工具類必須在Spring容器初始化完之後才能工作。
在現有的實現思路的基礎上,這個弊端立刻就顯現出來了,在自己重寫的WebContextListener運行的時候,Spring容器是沒有初始化的!所以會報空指針異常。
解決思路:工具類必須在Spring容器初始化完後纔可以正常工作,那麼可以找一個合適的位置開啓(僅開啓一次)任務調度器。