學習過sPRing框架的人一定都會聽過Spring的IoC(控制反轉) 、DI(依賴注入)這兩個概念,對于初學Spring的人來說,總覺得IoC 、DI這兩個概念是模糊不清的,是很難理解的,今天和大家分享網上的一些技術大牛們對Spring框架的IOC的理解以及談談我對Spring Ioc的理解。
首先要分享的是Iteye的開濤這位技術牛人對Spring框架的IOC的理解,寫得非常通俗易懂,以下內容全部來自原文,原文地址:http://jinnianshilongnian.iteye.com/blog/1413846
Ioc—Inversion of Control,即“控制反轉”,不是什么技術,而是一種設計思想。在java開發中,Ioc意味著將你設計好的對象交給容器控制,而不是傳統的在你的對象內部直接控制。如何理解好Ioc呢?理解好Ioc的關鍵是要明確“誰控制誰,控制什么,為何是反轉(有反轉就應該有正轉了),哪些方面反轉了”,那我們來深入分析一下:
●誰控制誰,控制什么:傳統Java SE程序設計,我們直接在對象內部通過new進行創建對象,是程序主動去創建依賴對象;而IoC是有專門一個容器來創建這些對象,即由Ioc容器來控制對 象的創建;誰控制誰?當然是IoC 容器控制了對象;控制什么?那就是主要控制了外部資源獲?。ú恢皇菍ο蟀ū热缥募龋?/strong>
●為何是反轉,哪些方面反轉了:有反轉就有正轉,傳統應用程序是由我們自己在對象中主動控制去直接獲取依賴對象,也就是正轉;而反轉則是由容器來幫忙創建及注入依賴對象;為何是反轉?因為由容器幫我們查找及注入依賴對象,對象只是被動的接受依賴對象,所以是反轉;哪些方面反轉了?依賴對象的獲取被反轉了。
用圖例說明一下,傳統程序設計如圖2-1,都是主動去創建相關對象然后再組合起來:
圖1-1 傳統應用程序示意圖
當有了IoC/DI的容器后,在客戶端類中不再主動去創建這些對象了,如圖2-2所示:
圖1-2有IoC/DI容器后程序結構示意圖
IoC%20不是一種技術,只是一種思想,一個重要的面向對象編程的法則,它能指導我們如何設計出松耦合、更優良的程序。傳統應用程序都是由我們在類內部主動創建依賴對象,從而導致類與類之間高耦合,難于測試;有了IoC容器后,把創建和查找依賴對象的控制權交給了容器,由容器進行注入組合對象,所以對象與對象之間是%20松散耦合,這樣也方便測試,利于功能復用,更重要的是使得程序的整個體系結構變得非常靈活。
其實IoC對編程帶來的最大改變不是從代碼上,而是從思想上,發生了“主從換位”的變化。應用程序原本是老大,要獲取什么資源都是主動出擊,但是在IoC/DI思想中,應用程序就變成被動的了,被動的等待IoC容器來創建并注入它所需要的資源了。
IoC很好的體現了面向對象設計法則之一——%20好萊塢法則:“別找我們,我們找你”;即由IoC容器幫對象找相應的依賴對象并注入,而不是由對象主動去找。
DI—Dependency%20Injection,即“依賴注入”:組件之間依賴關系由容器在運行期決定,形象的說,即由容器動態的將某個依賴關系注入到組件之中。依賴注入的目的并非為軟件系統帶來更多功能,而是為了提升組件重用的頻率,并為系統搭建一個靈活、可擴展的平臺。通過依賴注入機制,我們只需要通過簡單的配置,而無需任何代碼就可指定目標需要的資源,完成自身的業務邏輯,而不需要關心具體的資源來自何處,由誰實現。
理解DI的關鍵是:“誰依賴誰,為什么需要依賴,誰注入誰,注入了什么”,那我們來深入分析一下:
●誰依賴于誰:當然是應用程序依賴于IoC容器;
●為什么需要依賴:應用程序需要IoC容器來提供對象需要的外部資源;
●誰注入誰:很明顯是IoC容器注入應用程序某個對象,應用程序依賴的對象;
●注入了什么:就是注入某個對象所需要的外部資源(包括對象、資源、常量數據)。
IoC和DI由什么關系呢?其實它們是同一個概念的不同角度描述,由于控制反轉概念比較含糊(可能只是理解為容器控制對象這一個層面,很難讓人想到誰來維護對象關系),所以2004年大師級人物Martin%20Fowler又給出了一個新的名字:“依賴注入”,相對IoC%20而言,“依賴注入”明確描述了“被注入對象依賴IoC容器配置依賴對象”。
看過很多對Spring的Ioc理解的文章,好多人對Ioc和DI的解釋都晦澀難懂,反正就是一種說不清,道不明的感覺,讀完之后依然是一頭霧水,感覺就是開濤這位技術牛人寫得特別通俗易懂,他清楚地解釋了IoC(控制反轉)%20和DI(依賴注入)中的每一個字,讀完之后給人一種豁然開朗的感覺。我相信對于初學Spring框架的人對Ioc的理解應該是有很大幫助的。
首先想說說IoC(Inversion%20of%20Control,控制反轉)。這是spring的核心,貫穿始終。所謂IoC,對于spring框架來說,就是由spring來負責控制對象的生命周期和對象間的關系。這是什么意思呢,舉個簡單的例子,我們是如何找女朋友的?常見的情況是,我們到處去看哪里有長得漂亮身材又好的mm,然后打聽她們的興趣愛好、QQ號、電話號、public class MoAttack { public void cityGateAsk(){ //①演員直接侵入劇本 LiuDeHua ldh = new LiuDeHua(); ldh.responseAsk(”墨者革離!”); } } 我們會發現以上劇本在①處,作為具體角色飾演者的劉德華直接侵入到劇本中,使劇本和演員直接耦合在一起(圖3-1)。
一個明智的編劇在劇情創作時應圍繞故事的角色進行,而不應考慮角色的具體飾演者,這樣才可能在劇本投拍時自由地遴選任何適合的演員,而非綁定在劉德華一人身上。通過以上的分析,我們知道需要為該劇本主人公革離定義一個接口: 代碼清單3-2 MoAttack:引入劇本角色 Java代碼
public class MoAttack { public void cityGateAsk() { //①引入革離角色接口 GeLi geli = new LiuDeHua(); //②通過接口開展劇情 geli.responseAsk(”墨者革離!”); } }
在①處引入了劇本的角色——革離,劇本的情節通過角色展開,在拍攝時角色由演員飾演,如②處所示。因此墨攻、革離、劉德華三者的類圖關系如圖 3 2所示:
可是,從圖3 2中,我們可以看出MoAttack同時依賴于GeLi接口和LiuDeHua類,并沒有達到我們所期望的劇本僅依賴于角色的目的。但是角色最終必須通過具體的演員才能完成拍攝,如何讓LiuDeHua和劇本無關而又能完成GeLi的具體動作呢?當然是在影片投拍時,導演將LiuDeHua安排在GeLi的角色上,導演將劇本、角色、飾演者裝配起來(圖3-3)。
通過引入導演,使劇本和具體飾演者解耦了。對應到軟件中,導演像是一個裝配器,安排演員表演具體的角色。 現在我們可以反過來講解IoC的概念了。IoC(Inverse of Control)的字面意思是控制反轉,它包括兩個內容: 其一是控制其二是反轉 那到底是什么東西的“控制”被“反轉”了呢?對應到前面的例子,“控制”是指選擇GeLi角色扮演者的控制權;“反轉”是指這種控制權從《墨攻》劇本中移除,轉交到導演的手中。對于軟件來說,即是某一接口具體實現類的選擇控制權從調用類中移除,轉交給第三方決定。 因為IoC確實不夠開門見山,因此業界曾進行了廣泛的討論,最終軟件界的泰斗級人物Martin Fowler提出了DI(依賴注入:Dependency Injection)的概念用以代替IoC,即讓調用類對某一接口實現類的依賴關系由第三方(容器或協作類)注入,以移除調用類對某一接口實現類的依賴?!耙蕾囎⑷搿边@個名詞顯然比“控制反轉”直接明了、易于理解。 IoC的類型 從注入方法上看,主要可以劃分為三種類型:構造函數注入、屬性注入和接口注入。Spring支持構造函數注入和屬性注入。下面我們繼續使用以上的例子說明這三種注入方法的區別。 構造函數注入 在構造函數注入中,我們通過調用類的構造函數,將接口實現類通過構造函數變量傳入,如代碼清單3-3所示: 代碼清單3-3 MoAttack:通過構造函數注入革離扮演者 Java代碼
public class MoAttack { private GeLi geli; //①注入革離的具體扮演者 public MoAttack(GeLi geli){ this.geli = geli; } public void cityGateAsk(){ geli.responseAsk(”墨者革離!”); } }
MoAttack的構造函數不關心具體是誰扮演革離這個角色,只要在①處傳入的扮演者按劇本要求完成相應的表演即可。角色的具體扮演者由導演來安排,如代碼清單3-4所示: 代碼清單3-4 Director:通過構造函數注入革離扮演者 Java代碼
public class Director { public void direct(){ //①指定角色的扮演者 GeLi geli = new LiuDeHua(); //②注入具體扮演者到劇本中 MoAttack moAttack = new MoAttack(geli); moAttack.cityGateAsk(); } }
在①處,導演安排劉德華飾演革離的角色,并在②處,將劉德華“注入”到墨攻的劇本中,然后開始“城門叩問”劇情的演出工作。 屬性注入 有時,導演會發現,雖然革離是影片《墨攻》的第一主角,但并非每個場景都需要革離的出現,在這種情況下通過構造函數注入相當于每時每刻都在革離的飾演者在場,可見并不妥當,這時可以考慮使用屬性注入。屬性注入可以有選擇地通過Setter方法完成調用類所需依賴的注入,更加靈活方便: 代碼清單3-5 MoAttack:通過Setter方法注入革離扮演者 Java代碼
public class MoAttack { private GeLi geli; //①屬性注入方法 public void setGeli(GeLi geli) { this.geli = geli; } public void cityGateAsk() { geli.responseAsk(”墨者革離”); } }
MoAttack在①處為geli屬性提供一個Setter方法,以便讓導演在需要時注入geli的具體扮演者。 代碼清單3-6 Director:通過Setter方法注入革離扮演者 Java代碼
public class Director { public void direct(){ GeLi geli = new LiuDeHua(); MoAttack moAttack = new MoAttack(); //①調用屬性Setter方法注入 moAttack.setGeli(geli); moAttack.cityGateAsk(); } }
和通過構造函數注入革離扮演者不同,在實例化MoAttack劇本時,并未指定任何扮演者,而是在實例化MoAttack后,在需要革離出場時,才調用其setGeli()方法注入扮演者。按照類似的方式,我們還可以分別為劇本中其他諸如梁王、巷淹中等角色提供注入的Setter方法,這樣,導演就可以根據所拍劇段的不同,注入相應的角色了。 接口注入 將調用類所有依賴注入的方法抽取到一個接口中,調用類通過實現該接口提供相應的注入方法。為了采取接口注入的方式,必須先聲明一個ActorArrangable接口: Java代碼
public interface ActorArrangable { void injectGeli(GeLi geli); }
然后,MoAttack實現ActorArrangable接口提供具體的實現: 代碼清單3-7 MoAttack:通過接口方法注入革離扮演者 Java代碼
public class MoAttack implements ActorArrangable { private GeLi geli; //①實現接口方法 public void injectGeli (GeLi geli) { this.geli = geli; } public void cityGateAsk() { geli.responseAsk(”墨者革離”); } }
Director通過ActorArrangable的injectGeli()方法完成扮演者的注入工作。 代碼清單3-8 Director:通過接口方法注入革離扮演者 Java代碼
public class Director { public void direct(){ GeLi geli = new LiuDeHua(); MoAttack moAttack = new MoAttack(); moAttack. injectGeli (geli); moAttack.cityGateAsk(); } }
由于通過接口注入需要額外聲明一個接口,增加了類的數目,而且它的效果和屬性注入并無本質區別,因此我們不提倡采用這種方式。 通過容器完成依賴關系的注入 雖然MoAttack和LiuDeHua實現了解耦,MoAttack無須關注角色實現類的實例化工作,但這些工作在代碼中依然存在,只是轉移到Director類中而已。假設某一制片人想改變這一局面,在選擇某個劇本后,希望通過一個“海選”或者第三中介機構來選擇導演、演員,讓他們各司其職,那劇本、導演、演員就都實現解耦了。 所謂媒體“海選”和第三方中介機構在程序領域即是一個第三方的容器,它幫助完成類的初始化與裝配工作,讓開發者從這些底層實現類的實例化、依賴關系裝配等工作中脫離出來,專注于更有意義的業務邏輯開發工作。這無疑是一件令人向往的事情,Spring就是這樣的一個容器,它通過配置文件或注解描述類和類之間的依賴關系,自動完成類的初始化和依賴注入的工作。下面是Spring配置文件的對以上實例進行配置的配置文件片斷: xml代碼
<?xml version=“1.0” encoding=“UTF-8” ?> <beans xmlns=“http://www.springframework.org/schema/beans” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” xmlns:p=“http://www.springframework.org/schema/p” xsi:schemaLocation=”http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd”> <!–①實現類實例化–> <bean id=“geli” class=“LiuDeHua”/> <bean id=“moAttack” class=“com.baobaotao.ioc.MoAttack” p:geli-ref=“geli”/><!–②通過geli-ref建立依賴關系–> </beans>
通過new XmlBeanFactory(“beans.xml”)等方式即可啟動容器。在容器啟動時,Spring根據配置文件的描述信息,自動實例化Bean并完成依賴關系的裝配,從容器中即可返回準備就緒的Bean實例,后續可直接使用之。 Spring為什么會有這種“神奇”的力量,僅憑一個簡單的配置文件,就能魔法般地實例化并裝配好程序所用的Bean呢?這種“神奇”的力量歸功于Java語言本身的類反射功能。
新聞熱點
疑難解答