記得剛畢業那會兒,出來招工做被問到Spring的核心時,都以爲簡單的一筆,直接說不就是IoC(控制反轉)和DI(依賴注入)麼,而後省略一萬字對兩個名詞的解釋。最近空來整理了一下Spring中IoC的相關概念,便是複習,也是但願分享出來能幫助到你們更快理解IoC。html
其實IoC包括依賴查找(DL)和依賴注入(DI);只不過DL由於有侵入性 (它須要用戶本身去是使用 API 進行查找資源和組裝對象),已經被拋棄。因此如今提到IoC,更多的想到的就是依賴注入(DI)了。程序員
依賴注入(DI)包括Set注入和構造器注入!其實還有一個經過實現接口的方式實現依賴注入,不過不經常使用,就不說了。spring
如圖所示:編程
但其實 IOC 和DI 至關於一回事,只不過是看待問題的角度不一樣而已:
IOC: Spring 反向控制應用程序須要的資源。
DI: 應用程序依賴Spring爲其提供資源。
IOC 是站在Spring 的角度,而DI 是站在應用程序的角度。數據結構
以下圖所示:架構
接下來詳細介紹一下IoC,從其初始化到實現過程,細細理解!框架
IoC亦稱爲「依賴倒置原理」(Dependency Inversion Principle
),幾乎全部框架都使用了倒置注入(Martin Fowler
)技巧,是IoC原理的一項應用。SmaIITaIk
、C++
、Java
和.NET
面嚮對象語言的程序員已使用了這些原理。可是Spring是Java語言實現中最著名的一個。同時,控制反轉便是Spring框架的核心,也是Spring框架要解決的核心問題。工具
因爲不少對象的依賴關係和維護並不須要和系統運行狀態有很強的關聯性,因此能夠把在面向對象編程中須要執行的諸如新建對象、爲對象引用賦值等操做交由容器統一完成;這樣一來,這些散落在不一樣代碼中的功能相同的部分就集中成爲容器的一部分,也就是面向對象系統的基礎設施的一部分。同時,這些對象之間的相互依賴關係也是比較穩定的,通常不會隨着應用的運行狀態的改變而改變。性能
此時,IoC 容器控制了對象,咱們直接在對象內部經過new進行建立對象,是程序主動去建立依賴對象這是正轉,由於由容器幫咱們查找及注入依賴對象,對象只是被動的接受依賴對象,因此是反轉;這就解釋了控制反轉。.net
基於以上特性,這些對象使用IoC容器來管理,簡直就是天做之合。雖然這些特性存在於應用系統中,可是應用系統並不承擔管理這些對象的責任,而是經過依賴反轉把責任交給了容器(也能夠說是平臺)。
有了以上這些基礎知識儲備,Spring IoC容器的原理也就不難理解了。
在Spring中,Spring IoC提供了一個基本的JavaBean容器,經過IoC模式管理依賴關係,並經過依賴注入和AOP切面加強了爲JavaBea月這樣的POJO對象賦予事務管理、生命週期管理等基本功能。
Spring 做者 Rod Johnson設計了兩個接口用以表示容器:BeanFactory
和ApplicationContext
BeanFactory 粗暴簡單,能夠理解爲就是個 HashMap,Key 是 BeanName,Value 是 Bean 實例。一般只提供註冊(put),獲取(get)這兩個功能。咱們能夠稱之爲 「低級容器」。
ApplicationContext 能夠稱之爲 「高級容器」。由於他比 BeanFactory 多了更多的功能。他繼承了多個接口。所以具有了更多的功能。例如資源的獲取,支持多種消息(例如 JSP tag 的支持),對 BeanFactory 多了工具級別的支持等待。因此你看他的名字,已經不是 BeanFactory 之類的工廠了,而是 「應用上下文」, 表明着整個大容器的全部功能。該接口定義了一個 refresh
方法,此方法是全部閱讀 Spring 源碼的人的最熟悉的方法,用於刷新整個容器,即從新加載/刷新全部的 bean。
故咱們能夠認爲直接的BeanFactory實現是IoC容器的基本形式,而各類ApplicationContext的實現是IoC容器的高級表現形式。因此亦可簡單的把Spring IoC經過BeanFactory的實現當作低級容器;把ApplicationContext的實現當作高級容器。
此文咱們主要講解Spring 低級容器(BeanFactory)的 IoC;由於高級容器 ApplicationContext,它包含了低級容器的功能,當它執行 refresh 模板方法的時候,將刷新整個容器的 Bean。同時其做爲高級容器,它包含了太多的功能,不只僅是 IoC。它支持不一樣信息源頭,支持 BeanFactory 工具類、支持層級容器、支持訪問文件資源、支持事件發佈通知、支持接口回調等等。
圖片來源於:《Spring技術內幕:深刻解析Spring架構與設計原理(第2版)》
IoC 在 Spring 裏,只須要低級容器(BeanFactory)就能夠實現,兩個步驟:
值得注意的是,在這個過程當中,通常不包含Bean儂賴注入的實現。
在Spring IoC的設計中,Bean定義的載入和依賴注入是兩個獨立的過程。依賴注入通常發生在應用第一次經過getBean向容器索取Bean的時候。
但有一個例外值得注意,在使用loc容器時有一個預實例化的配置,經過這個預實例化的配置(具體來講,能夠經過爲Bean定義信息中的lazyinit屬性),用戶能夠對容器初始化過程做一個微小的控制,從而改變這個被設置了]azyinit屬性的Bean的依賴注入過程。
舉例來講,若是咱們對某個Bean設置了lazyinit屬性,那麼這個Bean的依賴注入在IoC容器初始化時就預先完成了,而不須要等到整個初始化完成之後,第一次使用getBean時纔會觸發。
對loC容器來講,它爲管理POJO之間的依賴關係提供了幫助,但也要依據Spring的定義規則提供Bean定義信息。咱們可使用各類形式的Bean定義信息,其中比較熟悉和經常使用的是使用XML的文件格式。
在Bean定義方面,Spring爲用戶提供了很大的靈活性。在初始化IoC容器的過程當中,首先須要定位到這些有效的Bean定義信息,這裏Spring使用Resource接口來統一這些Bean定義信息,而這個定位由ResourceLoader來完成。
信息的載入過程。對IoC容器來講,這個載入過程,至關於把定義的BeanDefinition在IoC容器中轉化成一個spring內部表示的數據結構的過程。IoC容器對Bean的管理和依賴注入功能的實現,都是經過對其持有的BeanDefinition進行各類相關操做來完成的。這些BeanDefinition數據在IoC容器中經過一個HashMap來保持和維護。固然這只是一種比較簡單的維護方式,若是須要提升IoC容器的性能和容量,徹底能夠本身作一些擴展。
IoC容器的初始化過程完成的主要工做是在IoC容器中創建BeanDefinition數據映射。但在此過程當中並無IoC容器對Bean依賴關係進行注入,那麼IoC容器是怎樣對Bean的依賴關係進行注入的呢?
假設當前IoC容器已經載入了用戶定義的Bean信息,開始分析依賴注入的原理:
首先,依賴注入的過程是用戶第一次向容器索要Bean時觸發的,固然也有例外,也就是咱們能夠在BeanDefinition信息中經過控制lazy-init屬性來讓容器完成對Bean的預實例化。這個預實例化實際上也是一個完成依賴注入的過程,但它是在初始化的過程當中完成的。
因此,當用戶向IoC容器索要Bean時,若是讀者還有印象,那麼必定還記得在基本的IoC容器接口BeanFactory中,有一個getBean的接口定義,這個接口的實現就是觸發依賴注入發生的地方(也就是依賴注入的入口);而依賴注入的發生是在容器中的BeanDefinition數據已經創建好的前提下才能完成的。
儘管能夠用最簡單的方式來描述IoC容器,將它視爲一個hashMap,但只能說這個hashMap是容器的最基本的數據結構,而不是IoC容器的所有。
打個比方來說,使用IoC後至關於IoC就是一個飲品店;之前的咱們須要本身new對象,也就是須要本身買橙子,買榨汁機來榨果汁喝;而是用IoC後,咱們只須要把需求(想喝橙汁)告訴它,而後由它給咱們提供橙汁就能夠了。這樣子想,是否是IoC就感受簡單多了呢?
Spring IoC容器做爲一個產品,其價值體如今一系列相關的產品特性上,這些產品特性以依賴反轉模式的實現爲核心,爲用戶更好地使用依賴反轉提供便利,從而實現了一個完整的IoC容器產品。