對於Java開發,現在Spring已成為一種生態,使用Spring成為一種享受,Spring的使用讓開發變得更加便捷。
而Spring好用歸好用,若不清楚里面的工作原理,使用的時候難免會踩到一些坑。
問題描述
在這里就說一下 注解的使用 ,相信有不少人遇到下面類似的問題:
“ @Autowired 注入為空 ”
“ @Resource 注入為空 ”
“ @PostConstruct 注解無效 ”
。。。。。。
這些問題幾乎都是在 非Controller層、非Service層 才會遇到的
不知道你們遇到這些問題的時候,有沒有想過為什么那些注解在 Controller、Service 里面才有效?
原理解析
其實這個得說到Spring的 IOC容器 對 Bean 的管理 。
在 Spring里面,能夠通過注解獲取到的 Bean 都是由 IOC容器 來管理獲取的。
在IOC容器啟動階段,IOC 會 通過 配置文件讀取、類掃描、注解識別 等等幾個步驟 ,來進行 Bean 的實例化 。其中,最關鍵的是注解識別,幾乎都是加了指定注解的類 才有可能被注冊到 IOC容器 ( 比如說 @Controller 、 @Service 、 @Component ...... ) 。同樣,也只有加了這些注解后,才能在類內正常使用 @Autowired 之類的注解 (因為在Bean實例化的時候,@Autowired 之類的注解會初始化Bean里面的變量)。而且 IOC容器里面的Bean都是單例模式,這也很好理解,如果有多個實例,當使用 @Autowired 獲取Bean的時候,怎么知道該返回哪個實例呢?
追根溯源
說到這,理解還不是很透徹的小伙伴,可能對前面舉出的注解問題,還沒想明白原因所在,“ 我也已經在類前面加了 @Component ,可是 @Autowired 的變量 還是空指針!這又是什么原因? ” 。
先舉個例子,
C 類 是需要 @Autowired 注入的變量 ,已證明在Controller、Service層都能使用注入正常 ;
B 類 是加了 @Component 的,並且使用了@Autowired 注入標識C類變量 ;
A 類 是通過 new B().method() 來調用 B類邏輯的 。
然后在 B類一個方法中使用C類變量 的時候,報了空指針異常!
如果你遇到的情況是這樣子的,那問題就在於 B類的被調用方式 。雖然 加了 @Component 注解,在容器初始化的時候,B類是已經被注冊到了 IOC容器的。
但由於 B類被調用的時候,是通過 new 的方式獲取的,這樣就違背了 IOC / DI 原則。所以B類無法被IOC容器當成單例去管理,也就是IOC容器發現后會把前面注冊在IOC的B類實例丟棄刪掉,即在IOC容器里面沒有了B類的實例。
那在調用 B類方法邏輯的時候,其中 @Autowired 注入的C類變量 就不會自動被初始化,你拿到的當然是空指針。
那怎么辦?注解使用很方便,我不想拋棄它,又希望在B類中能夠發揮它的作用。
兩個解決方法:
方法一:使用靜態變量存儲C類的實例
具體描述:在B類中,添加一個靜態變量, 使用 @PostConstruct 注解 初始化方法init() ,在init() 方法里面給靜態變量賦值 ,原本調用C類變量的地方使用靜態變量來代替。( @PostConstruct是在IOC啟動構造完成后執行的,那個時候B類還沒被拋出IOC容器 )
方法二:在B類調用的地方,修改其調用的方式,不能使用 new 來實例化調用
有關Spring注解的問題,到這里就說完了。至於哪個方法更好,就要根據實際情況自己判斷了。
共同學習,共同進步,若有補充,歡迎指出,謝謝!
