學(xué)習(xí)記錄之Spring-框架
Spring框架的作用
-主要解決了創(chuàng)建對象和管理對象的問題。
-在使用Spring框架后,當(dāng)需要某個對象時,直接通過Spring獲取此對象即可。
-Spring可以保證類的對象的唯一性,使得各組件出現(xiàn)依賴關(guān)系時,不會創(chuàng)建多個相同的對象。
-由于Spring會創(chuàng)建很多個對象并管理起來,開發(fā)者需要時直接獲取即可,所以,Spring也通常被稱之為“Spring容器”。
通過Spring創(chuàng)建并管理對象--組件掃描
????Spring提供了組件掃描的機制,它可以掃描指定的包,并查找此包及其子孫包下是否存在組件類,判定組件類的標(biāo)準(zhǔn)是類上是否存在組件注解,基礎(chǔ)的組件注解有:
- `@Component`
- `@Controller`
- `@Service`
- `@Repository`
在Spring框架的解釋中,以上4個注解是完全等效的,只是語義不同。所以,當(dāng)某個類需要被Spring框架創(chuàng)建對象時,需要:
1. 確保這個類在組件掃描的范圍之內(nèi)
2. 確保這個類添加了組件注解
提示:在Spring Boot項目中,默認(rèn)就已經(jīng)配置好了組件掃描,掃描的根包就是創(chuàng)建項目時已存在的包。
通過Spring創(chuàng)建并管理對象--@Bean方法
在Spring框架的使用中,如果某個類添加了`@Configuration`注解,則此類將是“配置類”。
注意:配置類也必須在組件掃描的范圍內(nèi)。
在配置類中,可以自行添加一些方法,在方法上添加`@Bean`注解,則此方法會被Spring自動調(diào)用,且方法返回的對象也會被Spring管理。
在同一個配置類中,允許存在多個`@Bean`方法。
關(guān)于方法的聲明:
- 訪問權(quán)限:應(yīng)該是`public`
- 返回值類型:你希望Spring管理的對象的類型
- 方法體:應(yīng)該正確的返回與聲明匹配的對象
- 方法名稱:自定義
所以,當(dāng)某個類需要被Spring框架管理對象時,需要:
- 在配置類中添加`@Bean`方法,使用此方法返回需要被管理的對象
如何選擇創(chuàng)建并管理對象的方式
僅自定義的類型可以使用組件掃描的方式,當(dāng)然,自定義的類型也可以使用`@Bean`方法的做法,但是不推薦。
非自定義的類型不可以使用組件掃描的方式(因為你沒有辦法在這些類型上添加組件注解),只能使用`@Bean`方法的做法。
自動裝配
當(dāng)某個屬性需要值時,或被Spring調(diào)用的方法需要參數(shù)值時,Spring框架會自動從容器中查找符合的對象,自動為屬性或方法的參數(shù)賦值。在屬性上,添加`@Autowired`注解,即可使得Spring嘗試自動裝配。
假設(shè)在項目中存在:
- `IBrandRepository`
- `BrandRepositoryImpl`
- `IBrandService`
- `BrandServiceImpl`
- `BrandController`
其依賴關(guān)系是:在`Service`組件中需要使用到`Repository`,在`Controller`組件中需要使用到`Service`。要實現(xiàn)以上目標(biāo),需要:
- 以上各類和接口都應(yīng)該在組件掃描的根包或其子孫包下
- `IBrandRepository`、`IBrandService`不需要添加注解,而`BrandRepositoryImpl`應(yīng)該添加`@Repository`注解,`BrandServiceImpl`添加`@Service`注解,`BrandController`添加`@Controller`注解
- 在`BrandServiceImpl`中聲明`BrandRepositoryImpl`類型的變量(暫時聲明為`public`)
- 【測試】在沒有添加`@Autowired`之前,此變量的值為`null`
- 【測試】當(dāng)已經(jīng)添加`@Autowired`之后,此變量的值為`BrandRepositoryImpl`類型的對象
- 使得`BrandRepositoryImpl`實現(xiàn)`IBrandRepository`接口
- 【測試】將`BrandServiceImpl`中,原本聲明為`BrandRepositoryImpl`類型的變量,改為`IBrandRepository`類型,再次觀察,此變量的值不變
- 在`BrandController`中聲明`BrandServiceImpl`類型的變量
- 【測試】在沒有添加`@Autowired`之前,此變量的值為`null`
- 【測試】當(dāng)已經(jīng)添加`@Autowired`之后,此變量的值為`BrandServiceImpl`類型的對象使得`BrandServiceImpl`實現(xiàn)`IBrandService`接口
?- 【測試】將`BrandController`中,原本聲明為`BrandServiceImpl`類型的變量,改為`IBrandService`類型,再次觀察,此變量的值不變
組件掃描:
在Spring框架中,通過`@ComponentScan`注解,即可配置組件掃描
在Spring Boot項目中,默認(rèn)即存在的類(有`main()`方法的那個類)上添加了`@SpringBootApplication`注解,此注解的源代碼中包含`@ComponentScan`。
在Spring Boot項目中,無論是執(zhí)行默認(rèn)即存在的類的`main()`方法,還是執(zhí)行帶`@SpringBootTest`注解的類中的測試方法,都會加載整個項目的配置,所以,組件掃描是已啟動的。
在`@SpringBootApplication`的聲明上有`@ComponentScan`,則`@SpringBootApplication`具有`@ComponentScan`的效果。
在`@SpringBootApplication`的聲明上有`@ComponentScan`,則`@ComponentScan`可稱之為`@SpringBootApplication`的**元注解**。
?關(guān)于@Autowired注解與@Resource注解的區(qū)別
在使用Spring框架時,在類的屬性上使用`@Autowired`或`@Resource`都可以實現(xiàn)自動裝配!
關(guān)于這2個注解,`@Autowired`是Spring框架定義的注解,`@Resource`注解是`javax`包中注解!
關(guān)于`@Autowired`的裝配機制是:
1. 先根據(jù)類型在Spring容器中查找匹配的對象,看看有多少個
2. 如果匹配類型的對象的數(shù)量為0,即沒有,取決于`@Autowired`的`required`屬性
? ?1. 如果`required=true`,則裝配失敗,在加載Spring時就會報錯
? ?2. 如果`required=false`,則放棄自動裝配,需要自動裝配的屬性的值為`null`,加載Spring時并不會報錯,但在后續(xù)的使用中,可能會出現(xiàn)NPE
3. 如果匹配類型的對象有且僅有1個,則直接裝配
4. 如果匹配類型的對象的數(shù)量超過1個(2個或更多個),將嘗試根據(jù)名稱來裝配
? ?1. 如果存在名稱匹配的對象,則成功裝配
? ?2. 如果不存在名稱匹配的對象,則裝配失敗,在加載Spring時就會報錯
關(guān)于`@Resource`的裝配機制是先嘗試根據(jù)名稱來裝配,如果成功,則裝配完成,如果失敗,會嘗試根據(jù)類型來裝配,如果仍失敗,則裝配失??!
從最終的體驗來看,這2個注解都可以實現(xiàn)相同的效果,使用`@Autowired`能成功裝配的,使用`@Resource`也可以,使用`@Autowired`無法裝配的,使用`@Resource`也無法裝配。
在使用方面,`@Autowired`和`@Resource`也存在不同:
- `@Resource`僅在屬性上可以實現(xiàn)自動裝配
- `@Autowired`可以添加在屬性上、方法上、構(gòu)造方法上,用于實現(xiàn)自動裝配的效果
事實上,在專業(yè)的開發(fā)工具中(例如IntelliJ IDEA),是不建議在屬性上使用`@Autowired`來實現(xiàn)自動裝配的,因為它們認(rèn)為這是不安全的做法,如果Spring沒有被正常加載,則此屬性將不會被自動裝配,則屬性的值為`null`,如果沒有使用其它手段解決此問題,在使用過程中就會出現(xiàn)NPE!
- 通常,并不存在Spring沒有被正常加載的情況,則以上問題基本上不會出現(xiàn),即使出現(xiàn),也是非常小概率事件
- 在某些測試中,確實可能不加載Spring環(huán)境,則存在以上關(guān)心的NPE風(fēng)險
通常,這些開發(fā)工具會建議使用構(gòu)造方法來注入屬性的值,例如,當(dāng)在屬性上使用`@Autowired`時,IntelliJ IDEA會提示:
以上提示的意思是:字段的注入是不推薦的
當(dāng)嘗試使用構(gòu)造方法注入時,代碼大概是:
以上做法可以實現(xiàn)與傳統(tǒng)在屬性上添加`@Autowired`完全相同的效果!這種做法是推薦的,因為無論是否加載Spring,要想創(chuàng)建此類的對象,就必須調(diào)用構(gòu)造方法,如果以上構(gòu)造方法是當(dāng)前類中唯一的構(gòu)造方法,就必須傳入值,則不會出現(xiàn)NPE問題(除非故意傳`null`值)。
以上使用構(gòu)造方法可以成功為屬性賦值,也源自于Spring的自動裝配機制,因為自動裝配指的是:**當(dāng)某個屬性需要值時,或被Spring調(diào)用的方法(通常是配置類中的@Bean方法,或構(gòu)造方法)需要參數(shù)值時,Spring框架會自動從容器中查找符合的對象,自動為屬性或方法的參數(shù)賦值。**
但是,使用構(gòu)造方法為屬性注入值是比較麻煩的,特別是需要裝配的屬性發(fā)生變化時,構(gòu)造方法需要一并調(diào)整,甚至,在某些類中,需要注入的屬性較多,則構(gòu)造方法需要較多參數(shù),與常規(guī)代碼的設(shè)計也是不符的!
總的來說,使用`@Autowired`語法簡潔,需要調(diào)整代碼時也非常方便,但是,存在NPE風(fēng)險(概率非常低),而使用構(gòu)造方法則非常安全,不會出現(xiàn)NPE,但是,語法相對麻煩,且調(diào)整代碼也很麻煩!由于使用`@Autowired`時出現(xiàn)NPE的概率太低了,所以,在一般的開發(fā)實踐中,仍是使用`@Autowired`居多!
另外,關(guān)于Spring調(diào)用構(gòu)造方法:
如果類中沒有顯式的定義構(gòu)造方法,則JAVA編譯器會在編譯期添加默認(rèn)構(gòu)造方法,且Spring會自動調(diào)用
如果類中有且僅有1個構(gòu)造方法,無論此構(gòu)造方法是有參數(shù)的,還是無參數(shù)的,Spring都會自動嘗試調(diào)用?
如果類中有多個構(gòu)造方法,Spring會嘗試調(diào)用帶`@Autowired`注解的構(gòu)造方法(應(yīng)該只有1個構(gòu)造方法添加了此注解),如果所有構(gòu)造方法都沒有`@Autowired`注解,則Spring會嘗試調(diào)用默認(rèn)的構(gòu)造方法(即無參數(shù)的構(gòu)造方法),如果所有構(gòu)造方法都沒有`@Autowired`注解且都有參數(shù),則Spring無法調(diào)用
自動裝配的名稱
????當(dāng)Spring嘗試實現(xiàn)自動裝配時,會從Spring容器中查找合適的對象,關(guān)于“合適”的判定標(biāo)準(zhǔn),首先,類型必須匹配,如果匹配類別的Bean有多個,必須保證名稱匹配才可以正確裝配。
關(guān)于名稱匹配:被自動裝配的屬性名稱,與Bean的名稱相同。
關(guān)于Bean的名稱:如果類名的第1個字母是大寫的,且第2個字母是小寫的,則Bean的名稱是將類名首字母改為小寫,例如`BrandRepositoryImpl`的Bean名稱就是`brandRepositoryImpl`,否則,Bean名稱就是類名。
關(guān)于名稱,還可以使用注解來指定名稱,例如:
或者:
Spring Bean的作用域
Spring管理的對象默認(rèn)都是**單例**的,可以使用`@Scope("prototype")`使之“**非單例**”。
單例:單一實例,具體表現(xiàn)為:在某一時間點,此類的對象最多只有1個。
需要注意:Spring Bean的特性與通過設(shè)計模式中的單例模式創(chuàng)建的對象相同,但是,Spring框架并沒有使用單例模式,所以,在描述時,把Spring Bean描述為單例模式是不對的。
當(dāng)Spring管理的對象是單例狀態(tài)時,默認(rèn)是**預(yù)加載**的(加載Spring環(huán)境時就會創(chuàng)建此類的對象,類似于設(shè)計模式中的單例模式的餓漢式),也可以使用`@Lazy`注解將其配置為**懶加載**的(加載Spring環(huán)境時并不會直接創(chuàng)建此類的對象,當(dāng)?shù)?次嘗試獲取此對象時,才會創(chuàng)建對象,類似于設(shè)計模式中的單例模式的懶漢式)。
Spring Bean的生命周期
在自定義的、被Spring管理的類中,可以自定義方法,在方法上添加`@PostConstruct`注解后,此方法將變?yōu)镾pring Bean生命周期中的**初始化方法**,則會在創(chuàng)建此類的對象之后,被Spring自動調(diào)用,還可以自定義方法,在方法上添加`@PreDestroy`注解后,此方法將變?yōu)镾pring Bean生命周期中的**銷毀方法**,則會在銷毀對象的前一刻,被Spring自動調(diào)用。
Spring IoC與DI
IoC:Inversion of Control,即:控制反轉(zhuǎn),表現(xiàn)為:交對象的控制權(quán)(創(chuàng)建權(quán)力、管理權(quán)力)交給框架。
DI:Dependency Injection,即:依賴注入,當(dāng)前類中的代碼需要通過另一個類的執(zhí)行來實現(xiàn),則當(dāng)前類依賴于另一個類,例如Controller依賴Service,Service依賴Mapper,Spring可以通過自動裝配等機制為依賴項賦值,由于在編寫的源代碼中并沒有使用到賦值符號(`=`),所以,這個行為叫作“注入”。
Spring框架通過DI完善的實現(xiàn)了IoC,所以,DI是一種實現(xiàn)手段,IoC是需要實現(xiàn)的目標(biāo)。
關(guān)于Spring框架的異常
無此Bean的異常,在異常信息中會提示到底是缺少哪個Bean,例如以下提示的`cn.tedu.csmall.server.controller.CategoryController`,然后,再結(jié)合你的配置來檢查!
創(chuàng)建對象的方式只有2種,要么是組件掃描,要么是`@Bean`方法,檢查對應(yīng)的代碼即可。
如果提示的類型并不是自已配置的,且應(yīng)該是正常的可用的,可考慮為依賴項錯誤的問題,
不唯一的Bean的異常,當(dāng)嘗試自動裝配時,匹配類型的Bean超過1個
????