Spring框架中IoC容器與DI依賴(lài)注入教程
一、Spring 是什么
我們通常所說(shuō)的 Spring 指的是 Spring Framework (Spring 框架),它是?個(gè)開(kāi)源框架,有著活躍而龐大的社區(qū),這就是它之所以能長(zhǎng)久不衰的原因。Spring ?持?泛的應(yīng)?場(chǎng)景,它可以讓 Java 企業(yè)級(jí)的應(yīng)用程序開(kāi)發(fā)起來(lái)更簡(jiǎn)單。
??句話(huà)概括 Spring:Spring 是包含了眾多工具方法的 IoC 容器。
那么問(wèn)題來(lái)了,什么是容器?什么又是 IoC 容器呢?
1.1 什么是容器
顧名思義,容器是用來(lái)容納某種物品的。
我們接觸過(guò)的容器都有哪些?
- List/Map -> 數(shù)據(jù)存儲(chǔ)容器
- Tomcat -> Web 容器
1.2 什么是 IoC
IoC = Inversion of Control 翻譯成中?是 “控制(權(quán))反轉(zhuǎn)” 的意思,也就是說(shuō) Spring 是?個(gè) “控制反轉(zhuǎn)” 的容器。怎么理解這句話(huà)呢?我們先從以下示例開(kāi)始 ~
二、理解 IoC
2.1 傳統(tǒng)程序開(kāi)發(fā)的問(wèn)題
假如,我們現(xiàn)在構(gòu)建?輛 “車(chē)” 的程序,我們的實(shí)現(xiàn)思路是這樣的:
構(gòu)建?輛車(chē) (Car Class),然而車(chē)需要依賴(lài)車(chē)身 (FrameWork Class),而車(chē)身需要依賴(lài)底盤(pán) (Bottom Class),而底盤(pán)需要依賴(lài)輪胎 (Tire Class),最終程序的實(shí)現(xiàn)代碼如下:(輪胎只有一個(gè) size 屬性)
package old; /** * 構(gòu)建“車(chē)” */ public class Car { private Framework framework; public Car(int size) { framework = new Framework(size); } public static void main(String[] args) { int size = 15; // 構(gòu)建并運(yùn)行車(chē) Car car = new Car(size); car.init(); } // 運(yùn)行 public void init() { System.out.println("Car init."); // 依賴(lài) framework init() 方法 framework.init(); } }
package old; /** * 車(chē)身 */ public class Framework { private Bottom bottom; public Framework(int size) { bottom = new Bottom(size); } public void init() { System.out.println("Framework init."); // 依賴(lài) Bottom:init() bottom.init(); } }
package old; /** * 底盤(pán) */ public class Bottom { private Tire tire; public Bottom(int size) { tire = new Tire(size); // 自己(創(chuàng))造 } public void init() { System.out.println("Bottom init."); // 依賴(lài):Tire:init() tire.init(); } }
package old; /** * 輪胎 */ public class Tire { // 尺寸 private int size = 17; // 材質(zhì)... // 花紋... // 顏色... // ... public Tire(int size) { this.size = size; } public void init() { System.out.println("Tire size:" + size); } }
以上程序中,輪胎屬性信息只有 size,然而隨著對(duì)車(chē)的需求量越來(lái)越大,個(gè)性化需求也會(huì)越來(lái)越多,這時(shí)候我們就需要加工具有多個(gè)屬性的輪胎。
而以上程序的問(wèn)題是:添加輪胎屬性時(shí),即最底層代碼改動(dòng)之后,整個(gè)調(diào)用鏈上的所有代碼 (所有類(lèi)) 都需要修改。
2.2 分析
如何解決上述問(wèn)題呢?
我們可以嘗試不在每個(gè)類(lèi)中自己創(chuàng)建下級(jí)類(lèi),如果自己創(chuàng)建下級(jí)類(lèi)就會(huì)出現(xiàn)當(dāng)下級(jí)類(lèi)發(fā)生改變操作,自己也要跟著修改。
我們只需要將原來(lái)由自己創(chuàng)建的下級(jí)類(lèi),改為傳遞的方式 (也就是注入的方式),因?yàn)槲覀儾恍枰诋?dāng)前類(lèi)中創(chuàng)建下級(jí)類(lèi)了,所以下級(jí)類(lèi)即使發(fā)生變化 (創(chuàng)建或減少參數(shù)),當(dāng)前類(lèi)本身也無(wú)需修改任何代碼,這樣就完成了程序的解耦。
解耦指的是解決了代碼的耦合性問(wèn)題,耦合性換?種叫法為程序相關(guān)性。好的程序代碼的耦合性 (代碼之間的相關(guān)性) 是很低的 ~~
這就好比我們打造?輛完整的汽車(chē),如果所有的配件都是自己造,那么當(dāng)客戶(hù)需求發(fā)?改變的時(shí)候,我們就要自己動(dòng)手來(lái)改了;但如果我們是把輪胎外包出去,那么我們只需要向代理工廠下訂單就行了,我們自身是不需要出力的。
2.3 控制反轉(zhuǎn)式程序開(kāi)發(fā)
基于以上思路,我們把調(diào)用汽車(chē)的程序示例改造?下,把創(chuàng)建子類(lèi)的方式,改為注入傳遞的方式,具體實(shí)現(xiàn)代碼如下:
package v2; public class TireV2 { private int size = 17; private String color = "黑色"; public TireV2(int size, String color) { this.size = size; this.color = color; } public void init() { System.out.println("Tire v2 size:" + size); } }
package v2; public class BottomV2 { private TireV2 tireV2; public BottomV2(TireV2 tireV2) { this.tireV2 = tireV2; } public void init() { System.out.println("Bottom v2 init."); tireV2.init(); } }
package v2; public class FrameworkV2 { private BottomV2 bottomV2; public FrameworkV2(BottomV2 bottomV2) { this.bottomV2 = bottomV2; } public void init() { System.out.println("Framework v2 init."); bottomV2.init(); } }
package v2; /** * 控制反轉(zhuǎn)的車(chē) */ public class CarV2 { private FrameworkV2 frameworkV2; // 依賴(lài) public CarV2(FrameworkV2 frameworkV2) { // frameworkV2 = new FrameworkV2(); // 自己創(chuàng)建(自己控制對(duì)象的生命周期) this.frameworkV2 = frameworkV2; // Car 構(gòu)造方法不會(huì)在創(chuàng)建 } public void init() { System.out.println("Car v2 init."); // 依賴(lài) framework:init() frameworkV2.init(); } }
啟動(dòng)類(lèi):
package v2; public class App { public static void main(String[] args) { // 程序調(diào)用 int size = 15; String color = "紅色"; TireV2 tireV2 = new TireV2(size, color); BottomV2 bottomV2 = new BottomV2(tireV2); FrameworkV2 frameworkV2 = new FrameworkV2(bottomV2); CarV2 carV2 = new CarV2(frameworkV2); carV2.init(); } }
代碼經(jīng)過(guò)以上調(diào)整,無(wú)論底層類(lèi)如何變化,整個(gè)調(diào)用鏈?zhǔn)遣挥米鋈魏胃淖兊?。這樣就完成了代碼之間的解耦,從而實(shí)現(xiàn)了更加靈活、通用的程序設(shè)計(jì)!
2.4 對(duì)比總結(jié)規(guī)律
在傳統(tǒng)的代碼中對(duì)象創(chuàng)建順序是:Car -> Framework -> Bottom -> Tire
改進(jìn)之后解耦的代碼的對(duì)象創(chuàng)建順序是:Tire -> Bottom -> Framework -> Car
傳統(tǒng)代碼是 Car 創(chuàng)建并控制了 Framework,F(xiàn)ramework 創(chuàng)建并控制了 Bottom,依次往下…而改進(jìn)之后的控制權(quán)發(fā)生了反轉(zhuǎn),不再是上級(jí)對(duì)象創(chuàng)建并控制下級(jí)對(duì)象了,而是下級(jí)對(duì)象注入當(dāng)前對(duì)象中,下級(jí)不再由上級(jí)類(lèi)控制了!這樣即使下級(jí)類(lèi)發(fā)生任何改變,當(dāng)前類(lèi)都是不受影響的,這就是典型的控制反轉(zhuǎn),也就是 IoC 的實(shí)現(xiàn)思想。
誰(shuí)調(diào)用我,就把控制權(quán)交給誰(shuí)!而不是自己來(lái)控制,不用自己去new對(duì)象
2.5 理解 Spring IoC
回到我們的主題 Spring,本?剛開(kāi)始咱們就講:Spring 是包含了多個(gè)工具方法的 IoC 容器,這就是對(duì) Spring 最核心的總結(jié)。“集成多個(gè)工具方法”這事咱們以后慢慢再談,那如何理解“Spring 是?個(gè) IoC 容器”這句話(huà)呢?
既然 Spring 是?個(gè) IoC(控制反轉(zhuǎn))容器,重點(diǎn)還在“容器”?字上,那么它就具備兩個(gè)最基礎(chǔ)的功能:
- 將對(duì)象存入到容器
- 從容器中取出對(duì)象
也就是說(shuō) 學(xué)習(xí) Spring 最核心的功能,就是學(xué)習(xí)將對(duì)象存入到 Spring 中,再?gòu)?Spring 中獲取對(duì)象的過(guò)程 ~
將對(duì)象存放到容器中的好處:將對(duì)象存儲(chǔ)在 IoC 容器相當(dāng)于將以后可能用到的所有工具都制作好并放到倉(cāng)庫(kù)中,需要的時(shí)候直接取就行了,用完再把它放回到倉(cāng)庫(kù)。而 new 對(duì)象的方式相當(dāng)于,每次需要工具了,才現(xiàn)做,用完就扔掉了也不會(huì)保存,下次再用的時(shí)候還得重新做,這就是 IoC 容器和普通程序開(kāi)發(fā)的區(qū)別。
Spring 是?個(gè) IoC 容器,即 對(duì)象創(chuàng)建和銷(xiāo)毀的權(quán)利都交給 Spring 來(lái)管理了,它本身又具備了存儲(chǔ)對(duì)象和獲取對(duì)象的能力。
三、DI 概念說(shuō)明
談到 IoC,不得不提的?個(gè)詞就是 “DI”,DI 是 Dependency Injection 的縮寫(xiě),翻譯成中?是 “依賴(lài)注入” 的意思。
所謂依賴(lài)注入,就是由 IoC 容器在運(yùn)行期間,動(dòng)態(tài)地將某種依賴(lài)關(guān)系注入到對(duì)象之中。 所以,依賴(lài)注入(DI)和控制反轉(zhuǎn)(IoC)是從不同的角度的描述的同?件事情,就是指通過(guò)引入 IoC 容器,利用依賴(lài)關(guān)系注?的方式,實(shí)現(xiàn)對(duì)象之間的解耦。
IoC 是“目標(biāo)”也是?種思想,而目標(biāo)和思想只是?種指導(dǎo)原則,最終還是要有可行的落地?案,而 DI 就屬于具體的實(shí)現(xiàn)。
比如說(shuō)我今天心情比較好,要吃一頓好的犒勞犒勞自己,那么“吃?頓好的”是思想和目標(biāo) (是 IoC),但最后我是吃海底撈還是必勝客…?這就是具體的實(shí)現(xiàn),就是 DI 。
四、總結(jié)
Ioc全稱(chēng)Inversion of Control,把創(chuàng)建對(duì)象的權(quán)利交給容器,對(duì)象的實(shí)例不再由調(diào)用者來(lái)創(chuàng)建,而是由容器來(lái)創(chuàng)建,容器會(huì)負(fù)責(zé)控制程序之間的關(guān)系,而不是由調(diào)用者的程序代碼直接控制。這樣,控制權(quán)由應(yīng)用代碼轉(zhuǎn)移帶了容器,控制權(quán)發(fā)生了反轉(zhuǎn),這就是控制反轉(zhuǎn)。它是spring框架的核心思想之一。
DI全稱(chēng)Dependency Injection,當(dāng)某個(gè)java 實(shí)例需要另一個(gè)java實(shí)例時(shí),創(chuàng)建被調(diào)用者的工作不是由調(diào)用者實(shí)現(xiàn),而是由spring容器來(lái)完成,然后注入調(diào)用者,因此稱(chēng)為依賴(lài)注入。
到此這篇關(guān)于Spring框架中IoC容器與DI依賴(lài)注入教程的文章就介紹到這了,更多相關(guān)Spring IoC與DI內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中拷貝list數(shù)組幾種常見(jiàn)的方法
這篇文章主要給大家介紹了關(guān)于Java中拷貝list數(shù)組幾種常見(jiàn)的方法,在Java中,List是一個(gè)接口,它有多個(gè)實(shí)現(xiàn)類(lèi),如ArrayList、LinkedList等,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-08-08java 鍵盤(pán)輸入的多種實(shí)現(xiàn)方法
java不像C中擁有scanf這樣功能強(qiáng)大的函數(shù),大多是通過(guò)定義輸入輸出流對(duì)象。常用的類(lèi)有BufferedReader,Scanner。2013-03-03SpringBoot項(xiàng)目部署到阿里云服務(wù)器的實(shí)現(xiàn)步驟
本文主要介紹了SpringBoot項(xiàng)目部署到阿里云服務(wù)器的實(shí)現(xiàn)步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06Springboot+Shiro+Jwt實(shí)現(xiàn)權(quán)限控制的項(xiàng)目實(shí)踐
如今的互聯(lián)網(wǎng)已經(jīng)成為前后端分離的時(shí)代,所以本文在使用SpringBoot整合Shiro框架的時(shí)候會(huì)聯(lián)合JWT一起搭配使用,具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09JAVA遞歸與非遞歸實(shí)現(xiàn)斐波那契數(shù)列
這篇文章主要為大家詳細(xì)介紹了JAVA遞歸與非遞歸實(shí)現(xiàn)斐波那契數(shù)列,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02Spring Boot集成SpringFox 3.0與Pageable參數(shù)處理方法
這篇文章主要介紹了Spring Boot集成SpringFox 3.0與Pageable參數(shù)處理,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-10-10JAVA的LIST接口的REMOVE重載方法調(diào)用原理解析
這篇文章主要介紹了JAVA的LIST接口的REMOVE重載方法調(diào)用原理解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10