談談對Spring IOC(控制反轉)的理解--轉 談談對Spring IOC(控制反轉)的理解--轉

談談對Spring IOC(控制反轉)的理解--轉

 

學習過Spring框架的人必定都會聽過Spring的IoC(控制反轉) 、DI(依賴注入)這兩個概念,對於初學Spring的人來講,總以爲IoC 、DI這兩個概念是模糊不清的,是很難理解的,今天和你們分享網上的一些技術大牛們對Spring框架的IOC的理解以及談談我對Spring Ioc的理解。html

1、分享Iteye的開濤對Ioc的精彩講解

  首先要分享的是Iteye的開濤這位技術牛人對Spring框架的IOC的理解,寫得很是通俗易懂,如下內容所有來自原文,原文地址:http://jinnianshilongnian.iteye.com/blog/1413846java

1.一、IoC是什麼

  Ioc—Inversion of Control,即「控制反轉」,不是什麼技術,而是一種設計思想。在Java開發中,Ioc意味着將你設計好的對象交給容器控制,而不是傳統的在你的對象內部直接控制。如何理解好Ioc呢?理解好Ioc的關鍵是要明確「誰控制誰,控制什麼,爲什麼是反轉(有反轉就應該有正轉了),哪些方面反轉了」,那咱們來深刻分析一下:spring

  ●誰控制誰,控制什麼:傳統Java SE程序設計,咱們直接在對象內部經過new進行建立對象,是程序主動去建立依賴對象;而IoC是有專門一個容器來建立這些對象,即由Ioc容器來控制對 象的建立;誰控制誰?固然是IoC 容器控制了對象;控制什麼?那就是主要控制了外部資源獲取(不僅是對象包括好比文件等)。數據庫

  ●爲什麼是反轉,哪些方面反轉了:有反轉就有正轉,傳統應用程序是由咱們本身在對象中主動控制去直接獲取依賴對象,也就是正轉;而反轉則是由容器來幫忙建立及注入依賴對象;爲什麼是反轉?由於由容器幫咱們查找及注入依賴對象,對象只是被動的接受依賴對象,因此是反轉;哪些方面反轉了?依賴對象的獲取被反轉了。編程

  用圖例說明一下,傳統程序設計如圖2-1,都是主動去建立相關對象而後再組合起來:設計模式

圖1-1 傳統應用程序示意圖框架

  當有了IoC/DI的容器後,在客戶端類中再也不主動去建立這些對象了,如圖2-2所示:函數

 

圖1-2有IoC/DI容器後程序結構示意圖post

1.二、IoC能作什麼

  IoC 不是一種技術,只是一種思想,一個重要的面向對象編程的法則,它能指導咱們如何設計出鬆耦合、更優良的程序。傳統應用程序都是由咱們在類內部主動建立依賴對象,從而致使類與類之間高耦合,難於測試;有了IoC容器後,把建立和查找依賴對象的控制權交給了容器,由容器進行注入組合對象,因此對象與對象之間是 鬆散耦合,這樣也方便測試,利於功能複用,更重要的是使得程序的整個體系結構變得很是靈活。學習

  其實IoC對編程帶來的最大改變不是從代碼上,而是從思想上,發生了「主從換位」的變化。應用程序本來是老大,要獲取什麼資源都是主動出擊,可是在IoC/DI思想中,應用程序就變成被動的了,被動的等待IoC容器來建立並注入它所須要的資源了。

  IoC很好的體現了面向對象設計法則之一—— 好萊塢法則:「別找咱們,咱們找你」;即由IoC容器幫對象找相應的依賴對象並注入,而不是由對象主動去找。

1.三、IoC和DI

  DI—Dependency Injection,即「依賴注入」組件之間依賴關係由容器在運行期決定,形象的說,即由容器動態的將某個依賴關係注入到組件之中依賴注入的目的並不是爲軟件系統帶來更多功能,而是爲了提高組件重用的頻率,併爲系統搭建一個靈活、可擴展的平臺。經過依賴注入機制,咱們只須要經過簡單的配置,而無需任何代碼就可指定目標須要的資源,完成自身的業務邏輯,而不須要關心具體的資源來自何處,由誰實現。

  理解DI的關鍵是:「誰依賴誰,爲何須要依賴,誰注入誰,注入了什麼」,那咱們來深刻分析一下:

  ●誰依賴於誰:固然是應用程序依賴於IoC容器

  ●爲何須要依賴:應用程序須要IoC容器來提供對象須要的外部資源

  ●誰注入誰:很明顯是IoC容器注入應用程序某個對象,應用程序依賴的對象

  ●注入了什麼:就是注入某個對象所須要的外部資源(包括對象、資源、常量數據)

  IoC和DI由什麼關係呢?其實它們是同一個概念的不一樣角度描述,因爲控制反轉概念比較含糊(可能只是理解爲容器控制對象這一個層面,很難讓人想到誰來維護對象關係),因此2004年大師級人物Martin Fowler又給出了一個新的名字:「依賴注入」,相對IoC 而言,「依賴注入」明確描述了「被注入對象依賴IoC容器配置依賴對象」。

  看過不少對Spring的Ioc理解的文章,好多人對Ioc和DI的解釋都晦澀難懂,反正就是一種說不清,道不明的感受,讀完以後依然是一頭霧水,感受就是開濤這位技術牛人寫得特別通俗易懂,他清楚地解釋了IoC(控制反轉) 和DI(依賴注入)中的每個字,讀完以後給人一種豁然開朗的感受。我相信對於初學Spring框架的人對Ioc的理解應該是有很大幫助的。

2、分享Bromon的blog上對IoC與DI淺顯易懂的講解

2.一、IoC(控制反轉)

  首先想說說IoC(Inversion of Control,控制反轉)。這是spring的核心,貫穿始終。所謂IoC,對於spring框架來講,就是由spring來負責控制對象的生命週期和對象間的關係。這是什麼意思呢,舉個簡單的例子,咱們是如何找女友的?常見的狀況是,咱們處處去看哪裏有長得漂亮身材又好的mm,而後打聽她們的興趣愛好、qq號、電話號、ip號、iq號………,想辦法認識她們,投其所好送其所要,而後嘿嘿……這個過程是複雜深奧的,咱們必須本身設計和麪對每一個環節。傳統的程序開發也是如此,在一個對象中,若是要使用另外的對象,就必須獲得它(本身new一個,或者從JNDI中查詢一個),使用完以後還要將對象銷燬(好比Connection等),對象始終會和其餘的接口或類藕合起來。

  那麼IoC是如何作的呢?有點像經過婚介找女友,在我和女友之間引入了一個第三者:婚姻介紹所。婚介管理了不少男男女女的資料,我能夠向婚介提出一個列表,告訴它我想找個什麼樣的女友,好比長得像李嘉欣,身材像林熙雷,唱歌像周杰倫,速度像卡洛斯,技術像齊達內之類的,而後婚介就會按照咱們的要求,提供一個mm,咱們只須要去和她談戀愛、結婚就好了。簡單明瞭,若是婚介給咱們的人選不符合要求,咱們就會拋出異常。整個過程再也不由我本身控制,而是有婚介這樣一個相似容器的機構來控制。Spring所倡導的開發方式就是如此,全部的類都會在spring容器中登記,告訴spring你是個什麼東西,你須要什麼東西,而後spring會在系統運行到適當的時候,把你要的東西主動給你,同時也把你交給其餘須要你的東西。全部的類的建立、銷燬都由 spring來控制,也就是說控制對象生存週期的再也不是引用它的對象,而是spring。對於某個具體的對象而言,之前是它控制其餘對象,如今是全部對象都被spring控制,因此這叫控制反轉。

2.二、DI(依賴注入)

  IoC的一個重點是在系統運行中,動態的向某個對象提供它所須要的其餘對象。這一點是經過DI(Dependency Injection,依賴注入)來實現的。好比對象A須要操做數據庫,之前咱們老是要在A中本身編寫代碼來得到一個Connection對象,有了 spring咱們就只須要告訴spring,A中須要一個Connection,至於這個Connection怎麼構造,什麼時候構造,A不須要知道。在系統運行時,spring會在適當的時候製造一個Connection,而後像打針同樣,注射到A當中,這樣就完成了對各個對象之間關係的控制。A須要依賴 Connection才能正常運行,而這個Connection是由spring注入到A中的,依賴注入的名字就這麼來的。那麼DI是如何實現的呢? Java 1.3以後一個重要特徵是反射(reflection),它容許程序在運行的時候動態的生成對象、執行對象的方法、改變對象的屬性,spring就是經過反射來實現注入的。

  理解了IoC和DI的概念後,一切都將變得簡單明瞭,剩下的工做只是在spring的框架中堆積木而已。

3、我對IoC(控制反轉)和DI(依賴注入)的理解

  在平時的java應用開發中,咱們要實現某一個功能或者說是完成某個業務邏輯時至少須要兩個或以上的對象來協做完成,在沒有使用Spring的時候,每一個對象在須要使用他的合做對象時,本身均要使用像new object() 這樣的語法來將合做對象建立出來,這個合做對象是由本身主動建立出來的,建立合做對象的主動權在本身手上,本身須要哪一個合做對象,就主動去建立,建立合做對象的主動權和建立時機是由本身把控的,而這樣就會使得對象間的耦合度高了,A對象須要使用合做對象B來共同完成一件事,A要使用B,那麼A就對B產生了依賴,也就是A和B之間存在一種耦合關係,而且是緊密耦合在一塊兒,而使用了Spring以後就不同了,建立合做對象B的工做是由Spring來作的,Spring建立好B對象,而後存儲到一個容器裏面,當A對象須要使用B對象時,Spring就從存放對象的那個容器裏面取出A要使用的那個B對象,而後交給A對象使用,至於Spring是如何建立那個對象,以及何時建立好對象的,A對象不須要關心這些細節問題(你是何時生的,怎麼生出來的我可不關心,能幫我幹活就行),A獲得Spring給咱們的對象以後,兩我的一塊兒協做完成要完成的工做便可。

  因此控制反轉IoC(Inversion of Control)是說建立對象的控制權進行轉移,之前建立對象的主動權和建立時機是由本身把控的,而如今這種權力轉移到第三方,好比轉移交給了IoC容器,它就是一個專門用來建立對象的工廠,你要什麼對象,它就給你什麼對象,有了 IoC容器,依賴關係就變了,原先的依賴關係就沒了,它們都依賴IoC容器了,經過IoC容器來創建它們之間的關係。

  這是我對Spring的IoC(控制反轉)的理解。DI(依賴注入)其實就是IOC的另一種說法,DI是由Martin Fowler 在2004年初的一篇論文中首次提出的。他總結:控制的什麼被反轉了?就是:得到依賴對象的方式反轉了。

4、小結

  對於Spring Ioc這個核心概念,我相信每個學習Spring的人都會有本身的理解。這種概念上的理解沒有絕對的標準答案,仁者見仁智者見智。若是有理解不到位或者理解錯的地方,歡迎廣大園友指正!

---------------------------------------------------------------------------------------------------------------------

引述:IoC(控制反轉:Inverse of Control)是Spring容器的內核,AOP、聲明式事務等功能在此基礎上開花結果。可是IoC這個重要的概念卻比較晦澀隱諱,不容易讓人望文生義,這不能不說是一大遺憾。不過IoC確實包括不少內涵,它涉及代碼解耦、設計模式、代碼優化等問題的考量,咱們打算經過一個小例子來講明這個概念。

經過實例理解IoC的概念 

    賀歲大片在中國已經造成了一個傳統,每到年末總有多部賀歲大片紛至沓來讓人目不暇接。在全部賀歲大片中,張之亮的《墨攻》算是比較出彩的一部。該片講述了戰國時期墨家人革離幫助梁國反抗趙國侵略的我的英雄主義故事,恢宏壯闊、渾雄凝重的歷史場面至關震撼。其中有一個場景:當劉德華所飾演的墨者革離到達梁國都城下,城上樑國守軍問到:「來者何人?」劉德華回答:「墨者革離!」咱們不妨經過一個Java類爲這個「城門叩問」的場景進行編劇,並藉此理解IoC的概念: 
代碼清單3-1  MoAttack:經過演員安排劇本 

Java代碼   收藏代碼
  1. public class MoAttack {  
  2.    public void cityGateAsk(){  
  3.         //①演員直接侵入劇本  
  4.        LiuDeHua ldh = new LiuDeHua();  
  5.        ldh.responseAsk("墨者革離!");  
  6.    }  
  7. }  


   咱們會發現以上劇本在①處,做爲具體角色飾演者的劉德華直接侵入到劇本中,使劇本和演員直接耦合在一塊兒(圖3-1)。 

   一個明智的編劇在劇情創做時應圍繞故事的角色進行,而不該考慮角色的具體飾演者,這樣纔可能在劇本投拍時自由地遴選任何適合的演員,而非綁定在劉德華一人身上。經過以上的分析,咱們知道須要爲該劇本主人公革離定義一個接口: 
代碼清單3-2  MoAttack:引入劇本角色 

Java代碼   收藏代碼
  1. public class MoAttack {  
  2.    public void cityGateAsk()  
  3.    {  
  4.         //①引入革離角色接口  
  5.        GeLi geli = new LiuDeHua();   
  6.          
  7.         //②經過接口開展劇情  
  8.        geli.responseAsk("墨者革離!");    
  9.    }  
  10. }  


   在①處引入了劇本的角色——革離,劇本的情節經過角色展開,在拍攝時角色由演員飾演,如②處所示。所以墨攻、革離、劉德華三者的類圖關係如圖 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代碼   收藏代碼
  1. public class MoAttack {  
  2.    private GeLi geli;  
  3.    //①注入革離的具體扮演者  
  4.    public MoAttack(GeLi geli){   
  5.        this.geli = geli;  
  6.    }  
  7.     public void cityGateAsk(){  
  8.        geli.responseAsk("墨者革離!");  
  9.    }  
  10. }  


    MoAttack的構造函數不關心具體是誰扮演革離這個角色,只要在①處傳入的扮演者按劇本要求完成相應的表演便可。角色的具體扮演者由導演來安排,如代碼清單3-4所示: 
代碼清單3-4  Director:經過構造函數注入革離扮演者 

Java代碼   收藏代碼
  1. public class Director {  
  2.    public void direct(){  
  3.         //①指定角色的扮演者  
  4.        GeLi geli = new LiuDeHua();    
  5.   
  6.         //②注入具體扮演者到劇本中  
  7.        MoAttack moAttack = new MoAttack(geli);   
  8.        moAttack.cityGateAsk();  
  9.    }  
  10. }  


   在①處,導演安排劉德華飾演革離的角色,並在②處,將劉德華「注入」到墨攻的劇本中,而後開始「城門叩問」劇情的演出工做。 

屬性注入 

   有時,導演會發現,雖然革離是影片《墨攻》的第一主角,但並不是每一個場景都須要革離的出現,在這種狀況下經過構造函數注入至關於每時每刻都在革離的飾演者在場,可見並不穩當,這時能夠考慮使用屬性注入。屬性注入能夠有選擇地經過Setter方法完成調用類所需依賴的注入,更加靈活方便: 
代碼清單3-5  MoAttack:經過Setter方法注入革離扮演者 

Java代碼   收藏代碼
  1. public class MoAttack {  
  2.     private GeLi geli;  
  3.      //①屬性注入方法  
  4.     public void setGeli(GeLi geli) {    
  5.         this.geli = geli;  
  6.     }  
  7.     public void cityGateAsk() {  
  8.         geli.responseAsk("墨者革離");  
  9.     }  
  10. }  


   MoAttack在①處爲geli屬性提供一個Setter方法,以便讓導演在須要時注入geli的具體扮演者。 
代碼清單3-6  Director:經過Setter方法注入革離扮演者 

Java代碼   收藏代碼
  1. public class Director {  
  2.    public void direct(){  
  3.        GeLi geli = new LiuDeHua();  
  4.        MoAttack moAttack = new MoAttack();  
  5.   
  6.         //①調用屬性Setter方法注入  
  7.        moAttack.setGeli(geli);   
  8.        moAttack.cityGateAsk();  
  9.    }  
  10. }  


   和經過構造函數注入革離扮演者不一樣,在實例化MoAttack劇本時,並未指定任何扮演者,而是在實例化MoAttack後,在須要革離出場時,才調用其setGeli()方法注入扮演者。按照相似的方式,咱們還能夠分別爲劇本中其餘諸如梁王、巷淹中等角色提供注入的Setter方法,這樣,導演就能夠根據所拍劇段的不一樣,注入相應的角色了。 

接口注入 

   將調用類全部依賴注入的方法抽取到一個接口中,調用類經過實現該接口提供相應的注入方法。爲了採起接口注入的方式,必須先聲明一個ActorArrangable接口: 

Java代碼   收藏代碼
  1. public interface ActorArrangable {  
  2.    void injectGeli(GeLi geli);  
  3. }  


   而後,MoAttack實現ActorArrangable接口提供具體的實現: 
代碼清單3-7  MoAttack:經過接口方法注入革離扮演者 

Java代碼   收藏代碼
  1. public class MoAttack implements ActorArrangable {  
  2.     private GeLi geli;  
  3.      //①實現接口方法  
  4.     public void injectGeli (GeLi geli) {    
  5.         this.geli = geli;         
  6.     }  
  7.     public void cityGateAsk() {  
  8.         geli.responseAsk("墨者革離");  
  9.     }  
  10. }  


     Director經過ActorArrangable的injectGeli()方法完成扮演者的注入工做。 
代碼清單3-8  Director:經過接口方法注入革離扮演者 

Java代碼   收藏代碼
  1. public class Director {  
  2.    public void direct(){  
  3.        GeLi geli = new LiuDeHua();  
  4.        MoAttack moAttack = new MoAttack();  
  5.        moAttack. injectGeli (geli);  
  6.        moAttack.cityGateAsk();  
  7.    }  
  8. }  


    因爲經過接口注入須要額外聲明一個接口,增長了類的數目,並且它的效果和屬性注入並沒有本質區別,所以咱們不提倡採用這種方式。 

經過容器完成依賴關係的注入 

   雖然MoAttack和LiuDeHua實現瞭解耦,MoAttack無須關注角色實現類的實例化工做,但這些工做在代碼中依然存在,只是轉移到Director類中而已。假設某一製片人想改變這一局面,在選擇某個劇本後,但願經過一個「海選」或者第三中介機構來選擇導演、演員,讓他們各司其職,那劇本、導演、演員就都實現解耦了。 
   所謂媒體「海選」和第三方中介機構在程序領域便是一個第三方的容器,它幫助完成類的初始化與裝配工做,讓開發者從這些底層實現類的實例化、依賴關係裝配等工做中脫離出來,專一於更有意義的業務邏輯開發工做。這無疑是一件使人嚮往的事情,Spring就是這樣的一個容器,它經過配置文件或註解描述類和類之間的依賴關係,自動完成類的初始化和依賴注入的工做。下面是Spring配置文件的對以上實例進行配置的配置文件片段: 

Xml代碼   收藏代碼
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.     xmlns:p="http://www.springframework.org/schema/p"  
  5.     xsi:schemaLocation="http://www.springframework.org/schema/beans   
  6.        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">  
  7.         <!--①實現類實例化-->  
  8.    <bean id="geli" class="LiuDeHua"/>  
  9.    <bean id="moAttack" class="com.baobaotao.ioc.MoAttack"   
  10.          p:geli-ref="geli"/><!--②經過geli-ref創建依賴關係-->  
  11. </beans>  

   經過new XmlBeanFactory(「beans.xml」)等方式便可啓動容器。在容器啓動時,Spring根據配置文件的描述信息,自動實例化Bean並完成依賴關係的裝配,從容器中便可返回準備就緒的Bean實例,後續可直接使用之。    Spring爲何會有這種「神奇」的力量,僅憑一個簡單的配置文件,就能魔法般地實例化並裝配好程序所用的Bean呢?這種「神奇」的力量歸功於Java語言自己的類反射功能。 

相關文章
相關標籤/搜索