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