摘要:對(duì)象之間耦合度過(guò)高的系統(tǒng),必然會(huì)出現(xiàn)牽一發(fā)而動(dòng)全身的情形。控制被反轉(zhuǎn)之后,獲得依賴對(duì)象的過(guò)程由自身管理變?yōu)榱擞扇萜髦鲃?dòng)注入。于是,他給控制反轉(zhuǎn)取了一個(gè)更合適的名字叫做依賴注入。
Spring還可以這么學(xué)--IoC(控制反轉(zhuǎn)) / DI(依賴注入)理解
聲明:文章的前三部分參考博文:https://www.cnblogs.com/Nouno...
這篇文章首發(fā)是在我的個(gè)人微信訂閱號(hào)每天學(xué)編程,關(guān)注我的微信訂閱號(hào)查看更多文章。
我們都知道,在采用面向?qū)ο蠓椒ㄔO(shè)計(jì)的軟件系統(tǒng)中,它的底層實(shí)現(xiàn)都是由N個(gè)對(duì)象組成的,所有的對(duì)象通過(guò)彼此的合作,最終實(shí)現(xiàn)系統(tǒng)的業(yè)務(wù)邏輯。
圖1:軟件系統(tǒng)中耦合的對(duì)象
如果我們打開(kāi)機(jī)械式手表的后蓋,就會(huì)看到與上面類似的情形,各個(gè)齒輪分別帶動(dòng)時(shí)針、分針和秒 針順時(shí)針旋轉(zhuǎn),從而在表盤上產(chǎn)生正確的時(shí)間。圖1中描述的就是這樣的一個(gè)齒輪組,它擁有多個(gè)獨(dú)立的齒輪,這些齒輪相互嚙合在一起,協(xié)同工作,共同完成某項(xiàng) 任務(wù)。我們可以看到,在這樣的齒輪組中,如果有一個(gè)齒輪出了問(wèn)題,就可能會(huì)影響到整個(gè)齒輪組的正常運(yùn)轉(zhuǎn)。
齒輪組中齒輪之間的嚙合關(guān)系,與軟件系統(tǒng)中對(duì)象之間的耦合關(guān)系非常相似。對(duì)象之間的耦合關(guān)系是無(wú)法避免的,也是必要的,這是協(xié)同工作的基礎(chǔ)。現(xiàn)在,伴隨著 工業(yè)級(jí)應(yīng)用的規(guī)模越來(lái)越龐大,對(duì)象之間的依賴關(guān)系也越來(lái)越復(fù)雜,經(jīng)常會(huì)出現(xiàn)對(duì)象之間的多重依賴性關(guān)系,因此,架構(gòu)師和設(shè)計(jì)師對(duì)于系統(tǒng)的分析和設(shè)計(jì),將面臨 更大的挑戰(zhàn)。對(duì)象之間耦合度過(guò)高的系統(tǒng),必然會(huì)出現(xiàn)牽一發(fā)而動(dòng)全身的情形。
圖2:對(duì)象之間復(fù)雜的依賴關(guān)系
耦合關(guān)系不僅會(huì)出現(xiàn)在對(duì)象與對(duì)象之間,也會(huì)出現(xiàn)在軟件系統(tǒng)的各模塊之間,以及軟件系統(tǒng)和硬件系統(tǒng)之間。如何降低系統(tǒng)之間、模塊之間和對(duì)象之間的耦合度,是軟件工程永遠(yuǎn)追求的目標(biāo)之一。為了解決對(duì)象之間的耦合度過(guò)高的問(wèn)題,軟件專家Michael Mattson提出了IOC理論,用來(lái)實(shí)現(xiàn)對(duì)象之間的“解耦”,目前這個(gè)理論已經(jīng)被成功地應(yīng)用到實(shí)踐當(dāng)中,很多的J2EE項(xiàng)目均采用了IOC框架產(chǎn)品Spring。
2. 什么是控制反轉(zhuǎn)(IoC)IOC是Inversion of Control的縮寫,多數(shù)書籍翻譯成“控制反轉(zhuǎn)”,還有些書籍翻譯成為“控制反向”或者“控制倒置”。
1996年,Michael Mattson在一篇有關(guān)探討面向?qū)ο罂蚣艿奈恼轮校紫忍岢隽薎OC 這個(gè)概念。對(duì)于面向?qū)ο笤O(shè)計(jì)及編程的基本思想,前面我們已經(jīng)講了很多了,不再贅述,簡(jiǎn)單來(lái)說(shuō)就是把復(fù)雜系統(tǒng)分解成相互合作的對(duì)象,這些對(duì)象類通過(guò)封裝以 后,內(nèi)部實(shí)現(xiàn)對(duì)外部是透明的,從而降低了解決問(wèn)題的復(fù)雜度,而且可以靈活地被重用和擴(kuò)展。IOC理論提出的觀點(diǎn)大體是這樣的:借助于“第三方”實(shí)現(xiàn)具有依 賴關(guān)系的對(duì)象之間的解耦,如下圖:
圖3:IOC解耦過(guò)程
大家看到了吧,由于引進(jìn)了中間位置的“第三方”,也就是IOC容器,使得A、B、C、D這4個(gè)對(duì)象沒(méi)有了耦合關(guān)系,齒輪之間的傳動(dòng)全部依靠“第三 方”了,全部對(duì)象的控制權(quán)全部上繳給“第三方”IOC容器,所以,IOC容器成了整個(gè)系統(tǒng)的關(guān)鍵核心,它起到了一種類似“粘合劑”的作用,把系統(tǒng)中的所有 對(duì)象粘合在一起發(fā)揮作用,如果沒(méi)有這個(gè)“粘合劑”,對(duì)象與對(duì)象之間會(huì)彼此失去聯(lián)系,這就是有人把IOC容器比喻成“粘合劑”的由來(lái)。
我們?cè)賮?lái)做個(gè)試驗(yàn):把上圖中間的IOC容器拿掉,然后再來(lái)看看這套系統(tǒng):
圖4:拿掉IoC容器后的系統(tǒng)
我們現(xiàn)在看到的畫面,就是我們要實(shí)現(xiàn)整個(gè)系統(tǒng)所需要完成的全部?jī)?nèi)容。這時(shí)候,A、B、C、D這4個(gè)對(duì)象之間已經(jīng)沒(méi)有了耦合關(guān)系,彼此毫無(wú)聯(lián)系,這樣 的話,當(dāng)你在實(shí)現(xiàn)A的時(shí)候,根本無(wú)須再去考慮B、C和D了,對(duì)象之間的依賴關(guān)系已經(jīng)降低到了最低程度。所以,如果真能實(shí)現(xiàn)IOC容器,對(duì)于系統(tǒng)開(kāi)發(fā)而言, 這將是一件多么美好的事情,參與開(kāi)發(fā)的每一成員只要實(shí)現(xiàn)自己的類就可以了,跟別人沒(méi)有任何關(guān)系!
我們?cè)賮?lái)看看,控制反轉(zhuǎn)(IOC)到底為什么要起這么個(gè)名字?我們來(lái)對(duì)比一下:
軟件系統(tǒng)在沒(méi)有引入IOC容器之前,如圖1所示,對(duì)象A依賴于對(duì)象B,那么對(duì)象A在初始化或者運(yùn)行到某一點(diǎn)的時(shí)候,自己必須主動(dòng)去創(chuàng)建對(duì)象B或者使用已經(jīng)創(chuàng)建的對(duì)象B。無(wú)論是創(chuàng)建還是使用對(duì)象B,控制權(quán)都在自己手上。
軟件系統(tǒng)在引入IOC容器之后,這種情形就完全改變了,如圖3所示,由于IOC容器的加入,對(duì)象A與對(duì)象B之間失去了直接聯(lián)系,所以,當(dāng)對(duì)象A運(yùn)行到需要對(duì)象B的時(shí)候,IOC容器會(huì)主動(dòng)創(chuàng)建一個(gè)對(duì)象B注入到對(duì)象A需要的地方。
通過(guò)前后的對(duì)比,我們不難看出來(lái):對(duì)象A獲得依賴對(duì)象B的過(guò)程,由主動(dòng)行為變?yōu)榱吮粍?dòng)行為,控制權(quán)顛倒過(guò)來(lái)了,這就是“控制反轉(zhuǎn)”這個(gè)名稱的由來(lái)。
2004年,Martin Fowler探討了同一個(gè)問(wèn)題,既然IOC是控制反轉(zhuǎn),那么到底是“哪些方面的控制被反轉(zhuǎn)了呢?”,經(jīng)過(guò)詳細(xì)地分析和論證后,他得出了答案:“獲得依賴對(duì) 象的過(guò)程被反轉(zhuǎn)了”??刂票环崔D(zhuǎn)之后,獲得依賴對(duì)象的過(guò)程由自身管理變?yōu)榱擞蒊OC容器主動(dòng)注入。于是,他給“控制反轉(zhuǎn)”取了一個(gè)更合適的名字叫做“依賴 注入(Dependency Injection)”。他的這個(gè)答案,實(shí)際上給出了實(shí)現(xiàn)IOC的方法:注入。所謂依賴注入,就是由IOC容器在運(yùn)行期間,動(dòng)態(tài)地將某種依賴關(guān)系注入到對(duì) 象之中。
所以,依賴注入(DI)和控制反轉(zhuǎn)(IOC)是從不同的角度的描述的同一件事情,就是指通過(guò)引入IOC容器,利用依賴關(guān)系注入的方式,實(shí)現(xiàn)對(duì)象之間的解耦。
我們舉一個(gè)生活中的例子,來(lái)幫助理解依賴注入的過(guò)程。大家對(duì)USB接口和USB設(shè)備應(yīng)該都很熟悉吧,USB為我們使用電腦提供了很大的方便,現(xiàn)在有很多的外部設(shè)備都支持USB接口。
圖5:USB接口和USB設(shè)備
現(xiàn)在,我們利用電腦主機(jī)和USB接口來(lái)實(shí)現(xiàn)一個(gè)任務(wù):從外部USB設(shè)備讀取一個(gè)文件。
電腦主機(jī)讀取文件的時(shí)候,它一點(diǎn)也不會(huì)關(guān)心USB接口上連接的是什么外部設(shè)備,而且它確實(shí)也無(wú)須知道。它的任務(wù)就是讀取USB接口,掛接的外部設(shè)備只要符 合USB接口標(biāo)準(zhǔn)即可。所以,如果我給電腦主機(jī)連接上一個(gè)U盤,那么主機(jī)就從U盤上讀取文件;如果我給電腦主機(jī)連接上一個(gè)外置硬盤,那么電腦主機(jī)就從外置 硬盤上讀取文件。掛接外部設(shè)備的權(quán)力由我作主,即控制權(quán)歸我,至于USB接口掛接的是什么設(shè)備,電腦主機(jī)是決定不了,它只能被動(dòng)的接受。電腦主機(jī)需要外部 設(shè)備的時(shí)候,根本不用它告訴我,我就會(huì)主動(dòng)幫它掛上它想要的外部設(shè)備,你看我的服務(wù)是多么的到位。這就是我們生活中常見(jiàn)的一個(gè)依賴注入的例子。在這個(gè)過(guò)程 中,我就起到了IOC容器的作用。
通過(guò)這個(gè)例子,依賴注入的思路已經(jīng)非常清楚:當(dāng)電腦主機(jī)讀取文件的時(shí)候,我就把它所要依賴的外部設(shè)備,幫他掛接上。整個(gè)外部設(shè)備注入的過(guò)程和一個(gè)被依賴的對(duì)象在系統(tǒng)運(yùn)行時(shí)被注入另外一個(gè)對(duì)象內(nèi)部的過(guò)程完全一樣。
我們把依賴注入應(yīng)用到軟件系統(tǒng)中,再來(lái)描述一下這個(gè)過(guò)程:
對(duì)象A依賴于對(duì)象B,當(dāng)對(duì)象 A需要用到對(duì)象B的時(shí)候,IOC容器就會(huì)立即創(chuàng)建一個(gè)對(duì)象B送給對(duì)象A。IOC容器就是一個(gè)對(duì)象制造工廠,你需要什么,它會(huì)給你送去,你直接使用就行了, 而再也不用去關(guān)心你所用的東西是如何制成的,也不用關(guān)心最后是怎么被銷毀的,這一切全部由IOC容器包辦。
在傳統(tǒng)的實(shí)現(xiàn)中,由程序內(nèi)部代碼來(lái)控制組件之間的關(guān)系。我們經(jīng)常使用new關(guān)鍵字來(lái)實(shí)現(xiàn)兩個(gè)組件之間關(guān)系的組合,這種實(shí)現(xiàn)方式會(huì)造成組件之間耦合。IOC 很好地解決了該問(wèn)題,它將實(shí)現(xiàn)組件間關(guān)系從程序內(nèi)部提到外部容器,也就是說(shuō)由容器在運(yùn)行期將組件間的某種依賴關(guān)系動(dòng)態(tài)注入組件中。
最后,我們用兩個(gè)實(shí)例來(lái)看DI的注入方式,代碼也是十分簡(jiǎn)單的!
通過(guò)構(gòu)造函數(shù)注入ClassB 類通過(guò)類構(gòu)造函數(shù)被注入到 ClassA 類中
ClassB.java文件
package com.wangc; public class ClassB { public ClassB() { System.out.println("hello,我在ClassB的構(gòu)造器里!"); } public void say() { System.out.println("hello,我是ClassB!"); } }
ClassA.java文件
package com.wangc; public class ClassA { private ClassB classB; public ClassA(ClassB classB) { System.out.println("hello,我在ClassA的構(gòu)造器里!"); this.classB = classB; } public void sayHello() { classB.say(); } }
Beans.xml文件
MainApp.java文件
package com.wangc; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MainApp { public static void main(String[] args) { ApplicationContext context =new ClassPathXmlApplicationContext("Beans.xml"); ClassA classA = (ClassA) context.getBean("classA"); classA.sayHello(); } }
運(yùn)行結(jié)果:
hello,我在ClassB的構(gòu)造器里! hello,我在ClassA的構(gòu)造器里! hello,我是ClassB!通過(guò)setter方法注入
當(dāng)容器調(diào)用一個(gè)無(wú)參的構(gòu)造函數(shù)或一個(gè)無(wú)參的靜態(tài) factory 方法來(lái)初始化你的 bean 后,通過(guò)容器在你的 bean 上調(diào)用設(shè)值函數(shù),基于設(shè)值函數(shù)的 DI 就完成了。 將上面例子中的ClassA.java文件和Beans.xml文件作如下改動(dòng),其他文件不變。
ClassA.java文件
package com.wangc; public class ClassA { private ClassB classB; public ClassB getClassB() { return classB; } public void setClassB(ClassB classB) { System.out.println("hello,我在setClassB里!"); this.classB = classB; } public void sayHello() { classB.say(); } }
Beans.xml文件
運(yùn)行結(jié)果:
hello,我在ClassB的構(gòu)造器里! hello,我在setClassB里! hello,我是ClassB!
QQ學(xué)習(xí)交流群:713479727
微信學(xué)習(xí)交流群:微信群加入方式【公眾號(hào)下方菜單欄-->學(xué)習(xí)資源-->微信群】
微信公眾號(hào):EverydayCoding 或掃描下方二維碼
歡迎大家加入。。。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/69323.html
摘要:使用的好處知乎的回答不用自己組裝,拿來(lái)就用。統(tǒng)一配置,便于修改。 前言 只有光頭才能變強(qiáng) 回顧前面: 給女朋友講解什么是代理模式 包裝模式就是這么簡(jiǎn)單啦 單例模式你會(huì)幾種寫法? 工廠模式理解了沒(méi)有? 在刷Spring書籍的時(shí)候花了點(diǎn)時(shí)間去學(xué)習(xí)了單例模式和工廠模式,總的來(lái)說(shuō)還是非常值得的! 本來(lái)想的是刷完《Spring 實(shí)戰(zhàn) (第4版)》和《精通Spring4.x 企業(yè)應(yīng)用開(kāi)發(fā)實(shí)戰(zhàn)》...
摘要:學(xué)習(xí)總結(jié)學(xué)習(xí)整理的一些筆記,很簡(jiǎn)單。大部分認(rèn)為和只是不同的叫法而已。依賴注入的兩種方式和注解使用注釋驅(qū)動(dòng)的功能源碼剖析 Spring IoC學(xué)習(xí)總結(jié) 學(xué)習(xí)spring Ioc整理的一些筆記,很簡(jiǎn)單。分享給大家。 IoC 基本概念 在這之前,我們先記住一句話。好萊塢原則:Dont call us, we will call you.其實(shí)這句話很恰當(dāng)?shù)匦稳萘朔崔D(zhuǎn)的意味;Ioc, Inve...
摘要:上面這部分代碼不變,還是通過(guò)在構(gòu)造器中傳入依賴的方式初始化依賴調(diào)用這里,調(diào)用方無(wú)需了解內(nèi)部對(duì)的依賴。而配置一般用于上自動(dòng)掃描并注入的代碼如下這里只給出直接在依賴對(duì)象上添加注解的形式,還可以通過(guò)構(gòu)造器和注入依賴,這里就不多說(shuō)了。 前言 相信所有面試java開(kāi)發(fā)的童鞋一定都被問(wèn)到過(guò)是否使用過(guò)Spring,是否了解其IOC容器,為什么不直接使用工廠模式,以及究竟IOC和DI區(qū)別在于哪里這種問(wèn)...
摘要:模塊負(fù)責(zé)的所有面向切面的功能??偨Y(jié)的統(tǒng)一管理,降低了對(duì)象之間的耦合對(duì)主流的框架提供了很好的集成支持提供眾多組件,事務(wù)管理,等具有高度可開(kāi)放性,開(kāi)發(fā)者可以自由選擇部分或全部主要使用工廠模式和代理模式。 聊完了Spring框架中最重要的兩種設(shè)計(jì)模式,我們來(lái)看一下Spring框架的模塊和結(jié)構(gòu)圖。 Spring框架的結(jié)構(gòu) 下圖是Spring官方給出的Spring框架的結(jié)構(gòu)圖。 showImg(...
摘要:的兩大核心機(jī)制是控制反轉(zhuǎn)和面向切面編程,對(duì)于初學(xué)者來(lái)講,搞清楚這兩個(gè)核心機(jī)制就掌握了的基本應(yīng)用。配置對(duì)象張三添加標(biāo)簽對(duì)應(yīng)屬性名,是屬性的值。若包含特殊字符,比如張三,使用張三進(jìn)行配置,如下所示。 前言 對(duì)于任何一個(gè) Java 開(kāi)發(fā)人員,Spring 的大名一定如雷貫耳,在行業(yè)中可謂是無(wú)人不知、無(wú)人不曉,說(shuō)它是 Java 領(lǐng)域第一框架毫不為過(guò)。 showImg(https://segme...
閱讀 866·2023-04-26 00:11
閱讀 2666·2021-11-04 16:13
閱讀 2116·2021-09-09 09:33
閱讀 1483·2021-08-20 09:35
閱讀 3836·2021-08-09 13:42
閱讀 3615·2019-08-30 15:55
閱讀 1074·2019-08-30 15:55
閱讀 2228·2019-08-30 13:55