?問題發生場景:
在使用 IDEA 開發 SpringBoot 項目時,在?Controller
?類中使用注解?@Autowired
?注入一個依賴出現了警告提示,查看其他使用該注解的地方同樣出現了警告提示。這是怎么回事?由于先去使用了SpringBoot并沒有對Spring進行系統性學習,所以做一個記錄。
Field injection is not recommended(不再推薦使用字段注入)?
因為功力有限,所以以下內容均來自別人的博客:
Field injection is not recommended(Spring團隊不推薦使用Field注入)_編程火箭車的博客-CSDN博客
為什么不推薦使用Field注入
- 違反單一責任原則
添加新的依賴項非常容易。添加6個、10個甚至12個依賴項沒有問題。當使用構造函數注入時,在某一點之后,構造函數參數的數量會變得過高,并且很明顯會出現問題。依賴太多通常意味著類有太多的責任。這可能違反了單一職責原則和關注點分離,這表明類需要進一步的檢查和重構。當直接注入字段時,沒有這樣的警告,因為這種方法可以無限擴展。
- 依賴隱藏
使用依賴注入容器意味著類不再負責管理自己的依賴項。獲取依賴項的職責是從類中提取的。由其他人現在負責提供依賴項——依賴注入容器或在測試中手動分配它們。當類不再負責獲取其依賴項時,它應該使用公共接口(方法或構造函數)清楚地與它們通信。這樣就可以清楚類需要什么,以及它是可選的(Setter)還是強制的(構造函數)。
- 依賴注入容器耦合
DI 框架的核心思想之一是托管類不應該依賴于所使用的 DI 容器。換句話說,它應該只是一個普通的 POJO,可以獨立地實例化它,前提是將所有必需的依賴項傳遞給它。通過這種方式,可以在單元測試中實例化它,而不需要啟動 DI 容器,并單獨測試它(使用的容器更像是集成測試)。如果沒有容器耦合,則可以將該類作為托管或非托管類使用,甚至可以切換到新的 DI 框架。
然而,當直接注入字段時,無法直接用所有需要的依賴項實例化類。這意味著:
- 有一種方法(通過調用默認構造函數)可以在一個狀態中使用 new 關鍵字來創建一個對象,該狀態中缺少一些強制協作者,使用將導致 NullPointerException。
- 這樣的類不能在 DI 容器(測試、其他模塊)之外重用,因為除了反射之外,沒有其他方法為它提供所需的依賴項。
- 不變性
與構造函數不同,Field 注入不能用于將依賴項分配給最終字段。
那Spring團隊推薦什么注入方式呢?
推薦構造器注入
官方文檔里的說法:Core Technologies
Constructor-based or setter-based DI?
Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for?mandatory dependencies?and setter methods or configuration methods for?optional dependencies. Note that use of the?@Required?annotation on a setter method can be used to make the property a required dependency.
The Spring team generally advocates constructor injection as it enables one to implement application components as?immutable objects?and to ensure that required dependencies are not?
null
. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state. As a side note, a large number of constructor arguments is a?bad code smell, implying that the class likely has too many responsibilities and should be refactored to better address proper separation of concerns.Setter injection should primarily only be used for optional dependencies that can be assigned reasonable default values within the class. Otherwise, not-null checks must be performed everywhere the code uses the dependency. One benefit of setter injection is that setter methods make objects of that class amenable to reconfiguration or re-injection later. Management through?JMX MBeans?is therefore a compelling use case for setter injection.
Use the DI style that makes the most sense for a particular class. Sometimes, when dealing with third-party classes for which you do not have the source, the choice is made for you. For example, if a third-party class does not expose any setter methods, then constructor injection may be the only available form of DI.
?最終要的是這句:
以下解釋來自:https://www.cnblogs.com/joemsu/p/7688307.html#_caption_2
The Spring team generally advocates constructor injection as it enables one to implement application components as?immutable objects?and to ensure that required dependencies are not?
null
. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state.
? 咳咳,再來簡單的翻譯一下:這個構造器注入的方式啊,能夠保證注入的組件不可變,并且確保需要的依賴不為空。此外,構造器注入的依賴總是能夠在返回客戶端(組件)代碼的時候保證完全初始化的狀態。
?
下面來簡單的解釋一下:
- 依賴不可變:其實說的就是final關鍵字
- 依賴不為空(省去了我們對其檢查):當要實例化FooController的時候,由于自己實現了有參數的構造函數,所以不會調用默認構造函數,那么就需要Spring容器傳入所需要的參數,所以就兩種情況:1、有該類型的參數->傳入,OK 。2:無該類型的參數->報錯。所以保證不會為空,Spring總不至于傳一個null進去吧 😦
- 完全初始化的狀態:這個可以跟上面的依賴不為空結合起來,向構造器傳參之前,要確保注入的內容不為空,那么肯定要調用依賴組件的構造方法完成實例化。而在Java類加載實例化的過程中,構造方法是最后一步(之前如果有父類先初始化父類,然后自己的成員變量,最后才是構造方法,這里不詳細展開。)。所以返回來的都是初始化之后的狀態。
結論
應盡量避免 Field 注入。推薦使用構造函數或方法來注入依賴項。兩者各有利弊,其用法取決于具體情況。但是,由于這些方法可以混合使用,所以這不是非必須選擇一種,可以將 Setter 和構造函數注入合并到一個類中。構造函數更適合于強制依賴項和以不變性為目標的情況。對于可選的依賴項,Setter 更好。
然后我在另一篇博客中也讀到,使用@Resource替代@Autowired就沒有這個提示了.....