目錄
什么是 IoC
IoC 介紹
傳統開發思路
解決方法
IoC 優勢
DI
IoC & DI 使用
IoC 詳解
Bean 的存儲
@Controller(控制器存儲)
獲取 bean 對象的其他方法
bean 命名
面試題之 ApplicationContext? pk BeanFactory
@Service(服務存儲)
@Respository(倉庫存儲)
@Component(組件存儲)
@Configuration(配置存儲)
為什么這么多類注解?
方法注解 @Bean
定義多個對象
掃描路徑
DI 詳解
屬性注入(Field Injection)
構造方法注入(Constructor Injection)
Setter 注入(Setter Injection)
三種注入方式的優缺點
@Autowired 存在的問題
使用@Primary:
?@Qualifier 注解:
@Resource 注解:
面試題之 @Autowird 與 @Resource 的區別
Spring,Spring Boot ,Spring MVC
完!
Spring 是包含了眾多工具方法的 IoC 容器
List/Map -> 數據存儲容器
Tomcat -> Web 容器
什么是 IoC
我們在前面已經使用過 IoC 了,在類上面添加 @RestController 和 @Controller 注解,就是把這個對象交給 Spring 管理,Spring 框架啟動的時候,就會加載該類,把對西昂交給 Spring 管理,就是 IoC 思想。
IoC:Inversion of Control(控制反轉),也就是說,Spring 是一個 “控制反轉” 的容器。
控制反轉什么呢??? 獲得依賴對象的過程被反轉了。也就是說,當需要某個對象的時候,傳統開發中,需要我們自己 new 一個對象,現在不需要再進行創建了,把創建對象的任務交給了容器。我們在程序中,只需要進行依賴注入即可~~ 這個容器,就稱為 IoC 容器,所以有時 Spring 也被稱為 Spring 容器。
IoC 介紹
我們以一個具體案例來了解 IoC 思想。
當我們需要造一輛車的時候。
傳統開發思路
先設計輪子,然后根據輪子的大小設計底盤,再根據底盤設計車身,再根據車身,對整個汽車進行設計。這樣一層一層設計,就會出現一個依賴關系 ,即汽車依賴車身,車身依賴底盤,底盤依賴輪子。
上面的設計的可維護性非常低。當我們的需求發生一些變化的時候,隨著對車的需求量越來越大,個性化需求也會越來越多,需要加工更多尺寸的輪胎,就需要對程序進行修改了。
即問題是:當最底層的代碼發生改動之后,調用鏈上的所有代碼,都需要進行修改。程序的耦合度極高,修改一處代碼,會影響其他處的代碼~~
解決方法
上面程序中,我們根據輪子的尺寸設計底盤,輪子尺寸修改,底盤就需要修改,同樣的,后面的車身,汽車都需要修改。
我們可以先設計汽車的大概樣子,然后根據汽車來設計車身,根據車身設計底盤,根據底盤設計輪子。
我們可以嘗試,不在每個類中創建自己的下積累,如果自己創建下級類,當下級類發生改變操作的時候,我們自己也要跟著修改。
改用傳遞(注入)的方式。
IoC 優勢
傳統代碼中創建對象的順序是:Car->Framework->Bottom->Tire
改進解耦合之后的順序是:Tire->Bottom->Framework->Car
改進之后,創建對象的控制權發生了反轉,不再是使用方來進行對象創建并且控制依賴對象了,而是把依賴對象注入到當前對象中,依賴對象的控制權不再由當前類進行控制了。
這部分代碼,就算 IoC 容器所作的工作。
IoC 容器可進行資源的幾種管理,實現資源的可配置和易管理,當我們需要使用的時候,只需要從容器中取就可以了
IoC 容器也降低了使用資源雙方的依賴程度,創建實例的時候并不需要了解細節,實現了解耦合~~
DI
DI:Dependency Injection (依賴注入)
容器在運行期間,動態的為應用程序提供運行時所依賴的資源。依賴注入是從應用程序的角度進行描述,指通過引入?IoC 容器,利用依賴關系注入的方式,實現對象之間的解耦合
IoC & DI 使用
Spring 是一個 IoC 容器,管理的主要是對象,這些對象,我們稱之為 Bean。Spring 來負責這些對象的創建和銷毀,我們程序只需要告訴 Spring,那些需要存,以及如何從 Spring 中取出對象。
實現:
1. 把 BookDao 交給 Spring 管理,在 BookDao 類前添加 @Component 注解
2. 把 BookService 交給 Spring 管理,由 Spring 來管理對象
3. 刪除 BookService 中創建 BookDao 的代碼,從 Spring 中獲取對象,添加 @Autowired 注解
4. 刪除 BookController 中創建?BookService 的代碼,從 Spring 中獲取對象,添加 @Autowried 注解
IoC 詳解
系統的學習 Spring IoC 和 DI 操作。前面提到的 IoC 控制反轉,就是將對象的控制權交給 Spring 的 IoC 容器,由它對對象進行創建和管理。
也就是 bean 的存儲。
Bean 的存儲
之前的案例中,我們要把對象交給 IoC 容器管理,需要在類上添加一個注解:@Component?
Spring 框架中,提供了更豐富的注解。
@Controller(控制器存儲)
從 Spring 容器中獲取對象
補充:因為我們的對象通過 @Controller 注解都交給 Spring 管理了,所以獲取對象要通過 Spring 獲取,那么就需要先得到 Spring 的上下文,即 ApplicationContext 翻譯就是 Spring 上下文~
此時,如果我們把 @Controller 刪掉,運行結果如下:
表明在 Spring 中找不到指定類型的 Bean
獲取 bean 對象的其他方法
通常使用 1 2 4 三種方式獲取 bean,且獲取到的 bean 都是一致的
其中 1 2 都涉及到根據名稱來獲取對象,再來研究一下 bean 命名約定~
bean 命名
官方文檔中寫道:程序開發人員并不需要為 bean 指定名稱,如果沒有顯示的提供名稱,Spring 容器會為該 bean 生成唯一的名稱。
命名約定就使用 Java 標準約定作為實例字段名。即,bean 名稱以小寫字母開頭,然后使用駝峰式大小寫~
也有特殊情況,即,當由多個字符,且第一個和第二個字符都是大寫的時候,將會保留原始的大小寫~
我們根據規則,再來進行 bean 的獲取:
三條 sout 語句的結果一致
地址一樣,說明對象是同一個~
面試題之 ApplicationContext? pk BeanFactory
繼承關系和功能方面:Spring 容器的兩個頂級的接口:BeanFactory 和 ApplicationContext。其中,BeanFactory 提供了基礎的訪問容器的能力,ApplicationContext 屬于 BeanFactory 的子類,出了繼承了 BeanFactory 的功能之外,還有了其他方面的特性~
性能方面:BeanFactory 是懶加載,用到那個加載那個。ApplicationContext 是一次性加載并初始化所有的 Bean 對象~
@Service(服務存儲)
去掉 @service 注解后,仍然報錯:
@Respository(倉庫存儲)
@Component(組件存儲)
@Configuration(配置存儲)
為什么這么多類注解?
@Controller:控制層,接收請求,對請求進行處理,并進行響應。
@Service:業務邏輯層,處理具體的業務邏輯。
@Respository:數據訪問層,負責數據訪問操作
@Configuration:配置層,處理項目中的一些配置信息
當我們對五大類注解的源碼進行觀察后
除 @component 之外,剩下四個注解的源碼中均有 @Component 注解。說明他們本身都是屬于 @Component 的子類,@Component 是一個元注解,可以對其他注解進行注解~~
剩下四個注解,可以被稱為 @Component 的衍生注解~~
其實從功能上來說,五大注解大差不多可以混用,但是在邏輯上是不可以的~~
方法注解 @Bean
類注解是添加到某個類上的,但有兩個問題:
????????1. 使用外部包中的類,沒辦法添加類注解
? ? ? ? 2. 一個類,需要多個對象,多個數據源~
這種情況,我們需要使用 @Bean 注解
?
結果如下:
方法注解是要配合類注解進行使用的。
在 Spring 框架的設計中,方法注解 @Bean 要配合類注解才能將對象正常的存儲到 Spring 容器中
定義多個對象
即,多數據源的情況,類是同一個,但配置不同,指向不同的數據源
此時,我們根據類型獲取對象:
運行結果:
期望是獲取到唯一一個匹配,結果發現了兩個 user1 和 user2
從報錯信息來分析,@Bean 注解的 bean,其名稱就是它的方法名。
根據名稱來獲取 bean 對象:
此時可以成功獲取到~
也可以對 bean 的名稱進行重命名
掃描路徑
Q:使用前面我們提到的四個注解生命的 bean,一定會生效嗎?
A:不一定(bean 要想生效,還需要被 Spring 掃描到)
其他代碼不懂,僅僅修改目錄結構:
又會發現,找不到名稱為 u1 的 bean。
使用五大注解聲明的 bean,要想生效,還需要配置掃描路徑,讓 Spring 掃描到這些注解~
使用 @ComponentScan 來配置掃描路徑
這種做法僅作了解~
那為什么前面我們并沒有配置這個直接,也可以正常使用呢?
@SpringBookApplication 這個啟動類聲明注解中,已經包含 @ComponentScan 注解了~
會默認掃描 SpringBoot 啟動類所在的包及其子包~
一般我們將啟動類直接放在我們要被掃描的包的路徑下即可~
DI 詳解
依賴注入是一個過程,值 IoC 容器在創建 Bean 的時候,去提供運行時所依賴的資源(對象)
上面案例中,我們是使用了? @Autowired 注解,完成了依賴注入的操作。
簡單來說,就是把對象取出來,放入某個類的屬性中。
依賴注入,Spring 也提供了三種方式
屬性注入(Field Injection)
屬性注入是使用 @Autowired 實現的,將 Service 類注入到 Controller 類中
運行結果:
去掉 @Autowired 的時候,結果如下:空指針異常,找不到 userService
構造方法注入(Constructor Injection)
注:
? ? ? ? 1. 如果只有一個構造函數的情況下,@Autowired 可以省略
? ? ? ? 2. 如果有多個構造函數,需要指定默認的構造函數(@Autowired 指定)如果未指定,則默認使用無參的構造函數
? ? ? ? 3. 構造函數規范:如果添加構造函數,把無參構造函數顯示的進行添加~
Setter 注入(Setter Injection)
Setter 注入和屬性的 Setter 方法實現類似,只不過在設置 set 方法時候,需要加上 @Autowired 注解
三種注入方式的優缺點
@Autowired 存在的問題
當同一個類中,存在多個 bean 的時候,使用 @Autowired 會存在問題
運行結果:
報錯的原因是:非唯一的 Bean 對象
解決方案:
@Primary
@Qualifier
@Resource
使用@Primary:
當存在多個相同類型的 Bean 注入時候,加上 @Primary 注解,來確認默認的實現~
?@Qualifier 注解:
指定當前要注入的 bean 對象。在 @Qualifier 的 value 屬性中,指定注入的 bean 的名稱
注:Qualifier 注解不能單獨使用,需要搭配 @Autowired 使用
@Resource 注解:
是按照 bean 的名稱進行注入。通過 name 屬性指定要注入的 bean 的名稱
面試題之 @Autowird 與 @Resource 的區別
1. @Autowired 是 Spring 框架提供的注解,而 @Resource 是 jdk 提供的注解
2 @Autowired 默認是按照類型注入的,而 @Resource 是按照名稱注入,相比 @Autowired 來說,@Resource 支持更多的參數設置~