昆明網(wǎng)站開發(fā)培訓(xùn)機(jī)構(gòu)整站優(yōu)化加盟
【JavaEE】Spring的開發(fā)要點(diǎn)總結(jié)(3)
文章目錄
- 【JavaEE】Spring的開發(fā)要點(diǎn)總結(jié)(3)
- 1. 屬性注入
- 1.1 @Autowired注解
- 1.2 依賴查找 VS 依賴注入
- 1.3 配合@Qualifier 篩選Bean對象
- 1.4 屬性注入的優(yōu)缺點(diǎn)
- 2. Setter注入
- 2.1 @Autowired注解
- 2.2 命名規(guī)則
- 2.3 Setter注入的優(yōu)缺點(diǎn)
- 3. 構(gòu)造方法注入
- 3.1 @Autowired注解
- 3.2 命名規(guī)則
- 3.3 構(gòu)造方法注入的優(yōu)缺點(diǎn)
- 4. 另一個(gè)注入可以用的注解@Resource
- 4.1 來源不同
- 4.2 匹配機(jī)制不同
- 4.3 參數(shù)不同
- 4.4 @Resource多一個(gè)匹配Bean對象名稱的方案
- 4.5 使用上的區(qū)別
- 5. 綜合練習(xí)
【JavaEE】Spring的開發(fā)要點(diǎn)總結(jié)(3)
在前面的代碼里,我們獲取Bean對象也比較麻煩:
本文章就是為了更方便地去獲取Bean對象~
- 對象裝配
- 也叫 對象注入
那么有沒有對應(yīng)的注解去實(shí)現(xiàn)這個(gè)功能呢?
Spring提供的三種實(shí)現(xiàn)方法:
- 屬性注入
- 構(gòu)造方法注入
- Setter注入
而這種非明文獲取Bean對象的過程,就是DI
- 而之前就是DL
首先,先創(chuàng)建一個(gè)新的項(xiàng)目,這次用規(guī)范的寫法(工程分層):
- 配置文件自己去配~
在對應(yīng)的層就是對應(yīng)的注解:
1. 屬性注入
這是數(shù)據(jù)持久層的一個(gè)插入方法,而 這個(gè)方法理論上是要在service層去調(diào)用的
但是new對象的寫法在Spring已經(jīng)不用啦:
屬性注入則可以更便捷獲取Bean對象~
1.1 @Autowired注解
自動接通吧,差不多那個(gè)意思~
利用這個(gè)注解,就能有以下操作:
這就是依賴注入,DI~
不用通過代碼顯示查找
- 而是在Spring中,隱式用高效的方式自動地掃描到對應(yīng)的Bean對象(n. 依賴)
- 然后賦值(v. 注入)給這個(gè)成員屬性
測試一下:
- new對象的方式不行,跟Bean對象是否加載存儲到Spring中五無關(guān)~
也可以通過單元測試的方式去測試代碼,后期更新測試開發(fā)博客文章的時(shí)候講解~
1.2 依賴查找 VS 依賴注入
參考之前的文章:
在這里體現(xiàn)出來的一點(diǎn)就是,依賴查找依賴Bean的名稱,而依賴注入則會自動匹配和找到Bean對象,注入到屬性中
- 不是說依賴注入沒有“查找”的過程,只是依賴注入重點(diǎn)在于注入
DI 流程是這樣的:
- getType,從容器中獲取對象
- 如果能拿到唯一一個(gè),那么就直接賦值給屬性變量里
- 這個(gè)時(shí)候變量名沒有要求~
- 正如上面寫的~
- 如果找到多個(gè),則根據(jù)名稱去匹配對應(yīng)的Bean對象!
測試結(jié)果:
- 確實(shí)是不同的Bean對象~
- 也驗(yàn)證了 DI 使用名稱去匹配~
- 這個(gè)時(shí)候你的變量名就必須是Bean對象其中一個(gè)的名稱了~
- 否則會報(bào)找不到,不唯一…的異常
1.3 配合@Qualifier 篩選Bean對象
你不管三七二十一,就想要一個(gè)響當(dāng)當(dāng)?shù)膶傩悦?#xff1a;“userSuperDao”,但是沒有這個(gè)名的Bean對象(現(xiàn)在有多個(gè)Bean)
你就可以利用這個(gè)注解:
1.4 屬性注入的優(yōu)缺點(diǎn)
參考鏈接: https://juejin.cn/post/7135235294265081887
優(yōu)點(diǎn):實(shí)現(xiàn)/使用簡單
缺點(diǎn):
- 無法注入到一個(gè)不可變的變量(final 修飾的變量)
原因是final的特征:
- 被final修飾的成員變量要在
- 定義時(shí)直接被賦值
- 構(gòu)造方法中第一次賦值
- 實(shí)例代碼塊中第一次賦值
- 除了成員變量外的final變量,要在第一次賦值后不能被修改
- 通用性問題
- 測試?yán)щy
- 依賴項(xiàng)之間的耦合
- 運(yùn)行時(shí)的驗(yàn)證困難
- 依賴關(guān)系的不清楚,復(fù)雜
- 我們這種簡單地用,@Autowired注解在屬性上,去注入的方式可能不適用與非IoC容器的
- 而Setter和構(gòu)造方法注入是通過調(diào)用方法的,所以沒有這個(gè)問題
- 設(shè)計(jì)原則問題:更容易違背單一設(shè)計(jì)原則
參考官方文檔:Spring | Home
單一設(shè)計(jì)原則要求一個(gè)類應(yīng)該只有一個(gè)引起改變的原因,即一個(gè)類應(yīng)該只有一個(gè)主要責(zé)任。當(dāng)使用屬性注入時(shí),可能會出現(xiàn)以下情況:
- 因?yàn)殚_銷比較小,在這個(gè)類里有啥需要就寫一個(gè)屬性,涉及多個(gè)職責(zé)耦合
- 依賴關(guān)系不清楚,亂,說到底還是屬性寫太多,設(shè)計(jì)不單一
但是,開發(fā)就是這樣,不會很完美,3這個(gè)點(diǎn)很難避開
- 有時(shí)候?yàn)榱怂俣?#xff0c;犧牲耦合性的情況也有
- 比如一個(gè)頁面的信息有分類的,有不同的職責(zé)
- 但是,如果訪問這個(gè)頁面,要去訪問那么多個(gè)接口,就有很多次“三次握手四次揮手”了
- 實(shí)際場景實(shí)際分析!
2. Setter注入
顧名思義,借此模仿,就是Setter方法加對應(yīng)的注解
2.1 @Autowired注解
這就自動在Spring中找到Bean對象,然后通過Setter注入給對應(yīng)的成員變量~
- DI,是這樣的
測試:
2.2 命名規(guī)則
命名規(guī)則跟屬性注入基本一致,你可以理解為屬性注入套了層皮,就是Setter注入
正好現(xiàn)在我有兩個(gè)UserDao的Bean對象,來演示一下:
- 一個(gè)aaa,一個(gè)userDao
觀察一下,再看答案:
- 跟方法名無關(guān)
- 跟屬性名無關(guān)
- 跟方法的參數(shù)名有關(guān)
- 可以結(jié)合@Qualifier注解
規(guī)則:
- 通過參數(shù)的類型,如果只獲得一個(gè)Bean對象,那么這個(gè)屬性的名字是啥都OK
- 如果獲取到多個(gè)Bean對象,則需要 通過屬性名進(jìn)行匹配
- @Qualifier去篩選,就不用考慮參數(shù)名的問題~
2.3 Setter注入的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
- 通常情況下,構(gòu)造Setter只去Set一兩個(gè)屬性,并不會構(gòu)造全部的Setter方法
所以Setter注入,更符合單一設(shè)計(jì)原則,就不會像屬性注入那樣的廣泛
通用性更好一點(diǎn)(相對于屬性注入)
- 屬性注入如果在別的容器上,失效了,但是Setter注入暴露出一個(gè)Set方法了呀,我們可以通過Set方法去挽救,自己手動賦值個(gè)Bean對象/new一個(gè)進(jìn)去~
- 而屬性注入的屬性,一般是private,在別的類不能直接修改其值
這個(gè)優(yōu)點(diǎn)很牽強(qiáng) ^ _ ^
缺點(diǎn):
- 無法注入到一個(gè)final修飾的變量
- Setter注入的對象可以被修改
- 相比于屬性注入,Setter方法是公開的,所以這個(gè)方法可能被多次調(diào)用并修改
- 這樣就導(dǎo)致注入的結(jié)果被覆蓋~
- 或者原本設(shè)置的值被注入修改~
- 屬性注入也可以,但是是private,所以只能在對應(yīng)的類中修改
3. 構(gòu)造方法注入
這是官方推薦的一種注入方式~
- 雖然如此,官方寫的代碼,用這個(gè)的不多🤣
- 雖然更加完美,但是寫起來麻煩
(Spring 4.x 之后推薦的注入方式)
3.1 @Autowired注解
- 在構(gòu)造方法上加上個(gè)@Autowired注解~
這也是標(biāo)準(zhǔn)的寫法~
不標(biāo)準(zhǔn)的寫法,不加@Autowired注解不會報(bào)錯(cuò):
- 因?yàn)楣俜酵扑],所以搞了點(diǎn)小特殊🤣
- 這個(gè)類如果只有一個(gè)構(gòu)造方法的情況下,才是可以省略的!
- 空的構(gòu)造方法還行,就相當(dāng)于啥也不注入,至少在構(gòu)造UserService3的時(shí)候可以調(diào)用這個(gè)方法
- 這個(gè)方法直接是不能調(diào)用了,因?yàn)椴淮_定調(diào)用哪個(gè)構(gòu)造方法,該注入啥
加上就不會有事:
原理就是:
- 框架在構(gòu)造UserService的時(shí)候,會根據(jù)@Autowried去挑選構(gòu)造方法,而參數(shù)的來源就是Bean對象
- 因此構(gòu)造方法就會將這些Bean對象注入到屬性上
所以不能有多個(gè)@Autowired修飾的構(gòu)造方法:
3.2 命名規(guī)則
跟Setter注入一樣!
- 只不過不能用@Qualifier注解!
- 這個(gè)類只有一個(gè)Bean對象,那么參數(shù)名無所謂
- 這個(gè)類有多個(gè)Bean對象,那么參數(shù)名必須要匹配對應(yīng)的Bean對象的名稱
3.3 構(gòu)造方法注入的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
- 可以注入到一個(gè)final修飾的變量
- 因?yàn)闃?gòu)造方法的第一次賦值final變量是允許的
- 但是一定要保證每個(gè)構(gòu)造方法涉及到的成員,都包含所有的final變量,即保證final變量都可以被初始化
- 一個(gè)都不能缺
運(yùn)行結(jié)果沒問題:
注入的對象不會被修改,因?yàn)闃?gòu)造方法只會加載一次
- 硬要修改還是可以的,只不過不能通過構(gòu)造方法
保證使用的時(shí)候,注入對象都被初始化了
通用性更好(相對其他)
- 同Setter注入
- 因?yàn)楸仨殏髦?#xff0c;因?yàn)閮?yōu)點(diǎn)3
缺點(diǎn):
- 可以傳入多個(gè)屬性,不太符合單一設(shè)計(jì)原則
- 這一點(diǎn)是寫代碼方面的初心問題吧。因?yàn)橛惨@么說,你完全也可以給那個(gè)別的屬性構(gòu)造個(gè)Setter呀
- 寫法比較復(fù)雜
- 無法解決循環(huán)依賴的問題
- a依賴b,b依賴a,就是一個(gè)“循環(huán)”
- 得用Spring三級緩存去解決
- 這個(gè)循環(huán)依賴后面詳細(xì)講,現(xiàn)在就了解一下~
常見的面試題就是三者的區(qū)別:就是把他們的使用和優(yōu)缺點(diǎn)講一下~
4. 另一個(gè)注入可以用的注解@Resource
這個(gè)可以直接替換@Autowired注解,用IDEA專業(yè)版的話,有些場景下,用@Autowired是會報(bào)錯(cuò)的~
- 但是屬于誤報(bào),運(yùn)行還是可以運(yùn)行(設(shè)置把報(bào)錯(cuò)信息忽略掉)~
Resource就沒啥問題:
用法基本一致:
這也是個(gè)常見的面試題:@Autowired和@Resource的區(qū)別是什么?
4.1 來源不同
@Autowired來自Spring框架,而@Resource來自JavaEE規(guī)范
4.2 匹配機(jī)制不同
- @Autowired先進(jìn)行類型匹配再用名稱匹配
- 但是專業(yè)版在這個(gè)注解判斷代碼正確與否的時(shí)候,并不會進(jìn)一步用名稱去匹配
- 所以會誤報(bào)
- @Resource先進(jìn)行名稱匹配再進(jìn)一步用類型匹配
- 名稱對不上,也會用類型去找,有多個(gè)Bean會報(bào)錯(cuò),只有一個(gè)Bean就忽略名稱的問題
- 當(dāng)然誤報(bào)的原因可能有很多,甚至不是單一的原因…
- IDEA兼容性不同~
4.3 參數(shù)不同
@Autowired只有一個(gè)參數(shù),required,而@Resource沒有這個(gè)參數(shù),但是有很多其他的參數(shù)
required默認(rèn)為true,代表要求Bean注入一定要存在
required設(shè)置為false,代表不要求Bean一定存在,不注入即可,就不會報(bào)找不到Bean對象異常~
4.4 @Resource多一個(gè)匹配Bean對象名稱的方案
因此,@Resource多了一個(gè)匹配名稱的方案:設(shè)置name的值即可:
4.5 使用上的區(qū)別
@Resource不支持構(gòu)造方法注入
5. 綜合練習(xí)
補(bǔ)充: 無論怎么樣,靜態(tài)屬性是無法被注入Bean的,因?yàn)殪o態(tài)屬性的加載是在Spring之前的,即類加載的時(shí)候,是這個(gè)類通用的屬性,而Bean對象是一個(gè)實(shí)例!難不成每次獲取Bean對象,Spring都會注入覆蓋這個(gè)值嗎~
這不合理~
- 而依賴注入發(fā)生在你獲取Bean對象的時(shí)候,Spring幫你構(gòu)造實(shí)例的時(shí)候,而不是你自己new和設(shè)置的時(shí)候
- 所以在main方法中,還是要通過DL去獲取Bean
要求:在Spring項(xiàng)目中,通過main方法獲取到Controller類,在Controller類里調(diào)用Service類,在Service類中獲取Dao類,在Dao類中的一個(gè)方法,用偽代碼new一個(gè)User,進(jìn)行返回,返回給main方法,打印user。
報(bào)錯(cuò)了,這也是常見的問題,就Test類并沒有在demo目錄下,而是與其并列,要把他放進(jìn)目錄里:
運(yùn)行結(jié)果:
是不是跟之前普通java代碼對IoC的理解思想差不多😀
【JavaEE】JavaEE進(jìn)階:框架的學(xué)習(xí) - Spring的初步認(rèn)識_s:103的博客-CSDN博客
文章到此結(jié)束!謝謝觀看
可以叫我 小馬,我可能寫的不好或者有錯(cuò)誤,但是一起加油鴨🦆!代碼鏈接:
- SpringDemo3 · 游離態(tài)/馬拉圈2023年7月 - 碼云 - 開源中國 (gitee.com)
- SpringDemo4 · 游離態(tài)/馬拉圈2023年7月 - 碼云 - 開源中國 (gitee.com)
用注解的方式去獲取Bean對象,是不是很方便🤭