學習過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 容器控制了對象;控制什麼?那就是主要控制了外部資源獲取(不僅是對象包括好比文件等)。數據庫
●爲什麼是反轉,哪些方面反轉了:有反轉就有正轉,傳統應用程序是由咱們本身在對象中主動控制去直接獲取依賴對象,也就是正轉;而反轉則是由容器來幫忙建立及注入依賴對象;爲什麼是反轉?由於由容器幫咱們查找及注入依賴對象,對象只是被動的接受依賴對象,因此是反轉;哪些方面反轉了?依賴對象的獲取被反轉了。apache
用圖例說明一下,傳統程序設計如圖2-1,都是主動去建立相關對象而後再組合起來:編程
圖1-1 傳統應用程序示意圖app
當有了IoC/DI的容器後,在客戶端類中再也不主動去建立這些對象了,如圖2-2所示:框架
圖1-2有IoC/DI容器後程序結構示意圖eclipse
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年初的一篇論文中首次提出的。他總結:控制的什麼被反轉了?就是:得到依賴對象的方式反轉了。
IoC是一個很大的概念,能夠用不一樣的方式來實現。主要的實現形式有兩種:
依賴查找:容器提供回調接口和上下文環境給組件。EJB和Apache Avalon都是使用這種方式。
依賴注入:組件不作定位查詢,只是提供普通的Java方法讓容器去決定依賴關係。容器全權負責組件的裝配,它會把符合依賴關係的對象經過JavaBean屬性或者構造子傳遞給須要的對象。經過JavaBean屬性注射依賴關係的作法稱爲設值方法注入(Setter Injection);將依賴關係做爲構造子參數傳入的作法稱爲構造子注入(Constructor Injection)。
附圖說明:
到這裏,你們應該對IoC與DI都有了初步的認識了。其實就Spring來講,就是JavaBean由Spring來管理組裝,表面上看就少了幾個new字,其實就是爲了下降耦合度,這也是咱們作軟件的目標之一。
至於Spring是怎樣實現IoC的,《expert one-on-one J2EE Development without EJB中文版》第七章「Spring框架介紹」很詳細的列舉了多種方法。說實在,一會兒看這麼多,我真有點糊塗了。我仍是本身寫個Demo熟悉一下大名鼎鼎的Spring吧。
首先得下載Spring。Spring網上有兩種Spring 包一種是spring-framework-1.2.6-with-dependencies.zip,另外一種是spring-framework-1.2.6.zip。固然最好是下載spring-framework-1.2.6-with-dependencies.zip形式的,由於裏面包括了更多的東東。spring-framework-1.2.6-with-dependencies.zip的下載地址是:http://prdownloads.sourceforge.net/springframework/spring-framework-1.2.6-with-dependencies.zip。
下載下來,解壓後,你會發現裏面有不少jar文件。由於剛剛接觸Spring,所以我只須要spring-core.jar(spring-framework-1.2.6\dist),將其導入eclipse的構建路徑中。另外,log日誌是須要的,這也是爲了養成良好的編程習慣。將log4j-1.2.9.jar(spring-framework-1.2.6\lib\log4j)和commons-logging.jar(spring-framework-1.2.6\lib\jakarta-commons)導入到構建路徑中。
準備就緒,開始寫Demo了。
個人工程的結構是:
一、 log4j.properties代碼:
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%c{1} - %m%n
如何使用Log4j,請看個人另外一篇轉貼的文章:如何使用Log4J。
二、 HelloBean的代碼:
這是一個簡單的JavaBean,有個String類型的helloworld屬性,初始值是"Hello!World!"。
三、 Bean.xml代碼:
<?xml version="1.0" encoding="GBK"?> <!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="helloBean" class="com.HelloBean"> <property name="helloworld"> <value>Hello!Rick</value> </property> </bean> </beans>
四、 Test的代碼:
package com; import org.springframework.beans.factory.*; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; public class Test { public static void main(String[] args) { //實例化JavaBean,主要是爲了比較new對象和依賴注入二者的區別 HelloBean hellobean=new HelloBean(); System.out.println(hellobean.getHelloworld()); //經過Spring訪問JavaBean組件 Resource resource=new ClassPathResource("com/bean.xml"); BeanFactory factory=new XmlBeanFactory(resource); hellobean=(HelloBean)factory.getBean("helloBean"); System.out.println(hellobean.getHelloworld()); } }
package com; import org.springframework.beans.factory.*; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; public class Test { public static void main(String[] args) { //實例化JavaBean,主要是爲了比較new對象和依賴注入二者的區別 HelloBean hellobean=new HelloBean(); System.out.println(hellobean.getHelloworld()); //經過Spring訪問JavaBean組件 Resource resource=new ClassPathResource("com/bean.xml"); BeanFactory factory=new XmlBeanFactory(resource); hellobean=(HelloBean)factory.getBean("helloBean"); System.out.println(hellobean.getHelloworld()); } }
這個Demo很好的闡述了Spring的Ioc,其實就Spring而言,就是經過配置文件,讓Spring如同一個管家同樣來管理全部的Bean類。
Spring的依賴注入相對複雜一點,主要是明白調用別的Bean,不是經過實例化對象來調用,而是告訴Spring,我須要什麼Bean,而後Spring再向你的Bean裏面注入你所須要的Bean對象。
接下來講說代碼實現,我只是在剛剛的例子上再添加一點東東。
首先要增長一個HelloImp的接口,這是問什麼呢,那你得問Spring,它定的規矩:JavaBean的實現要有兩個部分,一個接口,一個默認實現。你不照作就不行。
HelloImp代碼:
package com; public interface HelloImp { public void getName(); }
package com; public interface HelloImp { public void getName(); }
實現HelloImp的Hello代碼:
package com; public class Hello implements HelloImp { public void getName(){ System.out.println("Jack"); } }
package com; public class Hello implements HelloImp { public void getName(){ System.out.println("Jack"); } }
接着就是在HelloBean中調用Hello了。Spring的不一樣之處也在這體現出來。
package com; public class HelloBean { private String helloworld="Hello!World!"; private HelloImp hello; //注意這個私有對象是藉口 public String getHelloworld() { return helloworld; } public void setHelloworld(String helloworld) { this.helloworld = helloworld; } public void setHello(HelloImp hello) { this.hello = hello; } public void get(){ this.hello.getName(); } }
package com; public class HelloBean { private String helloworld="Hello!World!"; private HelloImp hello; //注意這個私有對象是藉口 public String getHelloworld() { return helloworld; } public void setHelloworld(String helloworld) { this.helloworld = helloworld; } public void setHello(HelloImp hello) { this.hello = hello; } public void get(){ this.hello.getName(); } }
注意字體加粗的地方。
配置文件也須要增長一點東西:
<?xml version="1.0" encoding="GBK"?> <!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <!—注意引用的類是具體的類Hello--> <bean id="myHello" class="com.Hello"> </bean> <bean id="helloBean" class="com.HelloBean"> <property name="helloworld"> <value>Hello!Rick</value> </property> <property name="hello"> <ref bean="myHello"></ref> </property> </bean> </beans>
<?xml version="1.0" encoding="GBK"?> <!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <!—注意引用的類是具體的類Hello--> <bean id="myHello" class="com.Hello"> </bean> <bean id="helloBean" class="com.HelloBean"> <property name="helloworld"> <value>Hello!Rick</value> </property> <property name="hello"> <ref bean="myHello"></ref> </property> </bean> </beans>
注意字體加粗的部分。
最後在Test中添加一句hellobean.get();就能夠看到結果了。
package com; import org.springframework.beans.factory.*; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; public class Test { public static void main(String[] args) { HelloBean hellobean=new HelloBean(); System.out.println(hellobean.getHelloworld()); Resource resource=new ClassPathResource("com/bean.xml"); BeanFactory factory=new XmlBeanFactory(resource); hellobean=(HelloBean)factory.getBean("helloBean"); System.out.println(hellobean.getHelloworld()); hellobean.get(); } }
package com; import org.springframework.beans.factory.*; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; public class Test { public static void main(String[] args) { HelloBean hellobean=new HelloBean(); System.out.println(hellobean.getHelloworld()); Resource resource=new ClassPathResource("com/bean.xml"); BeanFactory factory=new XmlBeanFactory(resource); hellobean=(HelloBean)factory.getBean("helloBean"); System.out.println(hellobean.getHelloworld()); hellobean.get(); } }