Spring IoC實(shí)現(xiàn)原理(附思維導(dǎo)圖)

本文適合初學(xué)者,也適合老手鞏固以前的知識(shí),針對(duì)IOC的知識(shí)點(diǎn)進(jìn)行詳細(xì)的講解。

在剛開始接觸IOC的時(shí)候,很多初學(xué)者回聽到很多名詞 IOC、DI、Spring IOC 等,往往聽的一頭霧水。所以在學(xué)習(xí)之前我們先梳理下這些概念,之后再去深入學(xué)習(xí) IoC 的實(shí)現(xiàn)原理部分。

很多人都知道 IOC,都知道 IOC 是 Spring 容器的內(nèi)核。

AOP、申明式事務(wù)等功能在此基礎(chǔ)上開發(fā)的。甚至在一提到 IOC 這個(gè)概念的時(shí)候就很自然的想到 Spring 容器,并將它們混為一談,認(rèn)為 IoC 就是 Spring IoC。這樣理解雖然并不妨礙我們使用 Spring 框架,但是兩者卻不是一回事。

IOC的概念和思想

IOC (Inversion Of Control 譯為 反轉(zhuǎn)控制),顧名思義,其一是控制,其二是反轉(zhuǎn)。即某一個(gè)接口具體實(shí)現(xiàn)類的選擇控制權(quán)從調(diào)用類中移除,轉(zhuǎn)交給第三方?jīng)Q定,在 Spring 容器中是由 Bean 配置來進(jìn)行控制的。

這段話比較繞口,簡單來說就是有了 IoC 之后你需要什么對(duì)象,IoC 容器幫你解決,你在家等著,IoC 容器主動(dòng)送貨上門。沒有 IoC 之前是自己親手去創(chuàng)建管理對(duì)象,現(xiàn)在直接現(xiàn)成的送過來。

再回頭看看,IoC 的 控制 和 反轉(zhuǎn) 這個(gè)兩個(gè)名詞就很好理解了:

再未使用 IoC 之前,需要使用一個(gè)對(duì)象需要直接 new 一個(gè)。那么你所依賴的對(duì)象就需要你自己去控制。但是有了 IoC 之后,直接由 IoC 容器來控制依賴的對(duì)象,當(dāng)你依賴一個(gè)對(duì)象的時(shí)候就由 IoC 容器創(chuàng)建后注入到被注入的對(duì)象中。

  • 控制表示對(duì)對(duì)象的控制
  • 反轉(zhuǎn)表示依賴對(duì)象的獲取反轉(zhuǎn)了

而實(shí)現(xiàn) IOC 概念的容器除了 Spring 還有 Guice,Jboss 等。

所以 IOC 和 Spring 中的 IOC 不能混為一談??梢哉f,IOC 本身只是一種概念和設(shè)計(jì)思想,Spring 容器實(shí)現(xiàn)了 IOC 思想,是 IOC 的實(shí)踐。

但由于 IOC 這種命名確實(shí)不夠開門見山,因此業(yè)界曾進(jìn)行了廣泛的討論,最終軟件界的泰斗級(jí)人物 Martin Fowler 提出了 DI(Dependency Injection,依賴注入)的概念用來替代 IoC。即讓調(diào)用類對(duì)某一接口實(shí)現(xiàn)類的依賴關(guān)系由第三方(容器或協(xié)作類)注入,以移除掉用類對(duì)某一接口實(shí)現(xiàn)類的依賴。

可以看出 DI 依賴注入的解釋 比 IoC 反轉(zhuǎn)控制 更直接明了,初學(xué)者更容易理解。

很多人將 IoC 和 DI 混為一談,這樣雖然也不影響使用,但至少要知道它由 IoC 變?yōu)?DI 的發(fā)展過程。

Spring IoC 的原理

Spring IoC 的底層實(shí)現(xiàn)是基于反射技術(shù),不了解這塊知識(shí)的同學(xué)可以看下這兩篇文章學(xué)習(xí)一下:《反射一開,誰都不愛》,《工廠模式的三種實(shí)現(xiàn),就這么簡單!》。

前面說了 IoC 容器是一個(gè)大的工廠來管理所有對(duì)象和它們的依賴關(guān)系,Spring 再處理這些對(duì)象和依賴關(guān)系也很簡單:

  • 使用反射技術(shù)獲取對(duì)象的信息包括:類信息、成員、方法等等
  • 再通過 xml 配置 或者 注解 的方式,說明依賴關(guān)系
  • 在調(diào)用類需要使用其他類的時(shí)候,不再通過調(diào)用類自己實(shí)現(xiàn),而是通過 IoC 容器進(jìn)行注入。

那 Spring 究竟是如何知道哪些對(duì)象是需要管理的呢?如何進(jìn)行管理的呢?又是如何進(jìn)行注入的呢?

Spring 通過一個(gè)配置文件或注解來描述 Bean 和 Bean 之間的依賴關(guān)系,根據(jù) Bean 配置信息在容器內(nèi)部創(chuàng)建Bean定義注冊(cè)表,根據(jù)注冊(cè)表加載、實(shí)例化 Bean、建立Bean與Bean之間的依賴關(guān)系,還提供了 Bean 實(shí)例緩存、生命周期管理、Bean 實(shí)例代理、事件發(fā)布、資源裝載等高級(jí)服務(wù)。

Spring 框架和核心接口是 Bean 工廠,工廠分為兩種:

  • BeanFactory:這是最基礎(chǔ)的,面向 Spring 的
  • ApplicationContext:是面向 Spring 框架開發(fā)者的,幾乎所有的應(yīng)用場合都可以使用它。

BeanFactory 有著龐大的繼承、實(shí)現(xiàn)體系,有眾多的子接口、實(shí)現(xiàn)類。

來看一下 BeanFactory 的基本類體系:

BeanFactory 的基本類體系

ApplicationContext 的繼承體系:

ApplicationContext 的繼承體系
  • BeanFactory 負(fù)責(zé)讀取bean配置文檔,管理bean的加載,實(shí)例化,維護(hù)bean之間的依賴關(guān)系,負(fù)責(zé)bean的聲明周期。

  • ApplicationContext 除了提供上述 BeanFactory 所能提供的功能之外,還提供了更完整的框架功能:

    1. 國際化支持
    2. 資源訪問:Resource rs = ctx. getResource(“classpath:config.properties”), “file:c:/config.properties”
    3. 事件傳遞:通過實(shí)現(xiàn)ApplicationContextAware接口

介紹了 Bean 的工廠類,再來詳細(xì)的解答下 Spring 是如何管理這些類的?這個(gè)問題。

Bean的生命周期

Bean 的生命周期可以說是面試的時(shí)候常問的問題之一,我們可以用下面兩張圖直觀的感受下:

BeanFactory 中 Bean 的生命周期:

BeanFactory 中 Bean 的生命周期

ApplicationContext 中 Bean 的生命周期:

ApplicationContext 中 Bean 的生命周期

可以看到在進(jìn)行實(shí)例化、設(shè)置屬性值、通過 init-method 屬性配置的初始化方法、放入 Spring 緩沖池 前后都有方法可以對(duì) Bean 進(jìn)行再次加工。

Bean 的生命周期從開始到最終銷毀其中經(jīng)歷的方法可以分為四類:

  • Bean自身的方法:如調(diào)用 Bean 構(gòu)造函數(shù)實(shí)例化 Bean,調(diào)用 Setter 設(shè)置 Bean 的屬性值以及通過的 init-method 和 destroy-method 所指定的方法;
  • Bean級(jí)生命周期接口方法:如 BeanNameAware、 BeanFactoryAware、 InitializingBean 和 DisposableBean,這些接口方法由 Bean 類直接實(shí)現(xiàn);
  • 容器級(jí)生命周期接口方法: InstantiationAwareBean PostProcessor 和 BeanPostProcessor 這兩個(gè)接口實(shí)現(xiàn),一般稱它們的實(shí)現(xiàn)類為“ 后處理器” 。這些后處理器的影響是全局性的。用戶可以通過合理地編寫后處理器,讓其僅對(duì)感興趣Bean 進(jìn)行加工處理。
  • 工廠后處理器接口方法

Bean 的裝配過程

簡單敘述下 Bean 的裝配過程:

  • BeanDefinitionReader 讀取 Resource 所指向的配置文件資源,然后解析配置文件。配置文件中每一個(gè)解析成一個(gè) BeanDefinition 對(duì)象,并保存到 BeanDefinitionRegistry 中;
  • 容器掃描 BeanDefinitionRegistry 中的BeanDefinition;調(diào)用InstantiationStrategy 進(jìn)行Bean實(shí)例化的工作;使用 BeanWrapper 完成Bean屬性的設(shè)置工作;
  • 單例Bean緩存池:Spring 在DefaultSingletonBeanRegistry類中提供了一個(gè)用于緩存單實(shí)例 Bean 的緩存器,它是一個(gè)用HashMap實(shí)現(xiàn)的緩存器,單實(shí)例的 Bean 以beanName為鍵保存在這個(gè)HashMap中。

ApplicationContext 和 BeanFactory 的區(qū)別

區(qū)別:

  • BeanFactory 在啟動(dòng)的時(shí)候不會(huì)去實(shí)例化 Bean,中有從容器中拿 Bean 的時(shí)候才會(huì)去實(shí)例化,ApplicationContext 在啟動(dòng)的時(shí)候就把所有的 Bean 全部實(shí)例化了。它還可以為 Bean 配置 lazy-init=true 來讓 Bean 延遲實(shí)例化;
  • BeanFacotry 是 spring 中比較原始的 Factory,無法支持 spring 的許多插件,如 AOP 功能、Web 應(yīng)用等。 ApplicationContext接口
    • MessageSource, 提供國際化的消息訪問
    • 資源訪問,如URL和文件
    • 事件傳播
    • 載入多個(gè)(有繼承關(guān)系)上下文 ,使得每一個(gè)上下文都專注于一個(gè)特定的層次,比如應(yīng)用的web層

Bean的裝配方式

Bean 的裝配方式有四種:

  • 基于 xml 的配置
  • 注解方式
  • java類的配置
  • 基于Groovy DSL的配置(不太常見)

一般都是用 xml 和 注解 兩種配置,其他的方式使用的不多。這里不多做敘述

依賴注入的方式

依賴注入的方式有三種方式:

  • 屬性注入:使用setter方法
  • 構(gòu)造函數(shù)注入
  • 工廠方法注入

這其中屬性注入的方式比較常用。

對(duì)象之間關(guān)系

對(duì)象之間有三種關(guān)系:

  • 依賴:挺少用的(在 xml 中使用depends-on就是依賴關(guān)系了-->前置依賴【依賴的Bean需要初始化之后,當(dāng)前Bean才會(huì)初始化】)
  • 繼承:
  • 引用:最常見(使用ref就是引用關(guān)系了)

Bean的作用域

  • singleton:單例,只有一個(gè)對(duì)象
  • prototype:多例,每次都會(huì)創(chuàng)建一個(gè)新對(duì)象
  • request:每次請(qǐng)求都會(huì)參數(shù)一個(gè)對(duì)象
  • session:每個(gè)session都會(huì)產(chǎn)生一個(gè)對(duì)象
  • global session:所有的session都用一個(gè)對(duì)象

默認(rèn)Bean的作用域是單例的,其他需要使用不同類型的作用域,需要單獨(dú)配置。

這里有個(gè)知識(shí)點(diǎn) @lookup注解需要注意。如果我們需要在 singleton 的 Bean 中注入一個(gè) prototype 的 Bean 并希望在每次調(diào)用 singleton 的 Bean 的時(shí)候獲取的都是 prototype 的 Bean ?這就需要使用 @lookup注解。

IOC 的優(yōu)缺點(diǎn)

  • 優(yōu)點(diǎn):實(shí)現(xiàn)組件之間的解耦,提高程序的靈活性和可維護(hù)性。
  • 缺點(diǎn):生成一個(gè)對(duì)象的步驟變復(fù)雜了,生成因?yàn)槭鞘褂梅瓷渚幊蹋谛噬嫌行p耗。但相對(duì)于IoC提高的維護(hù)性和靈活性來說,這點(diǎn)損耗是微不足道的,除非某對(duì)象的生成對(duì)效率要求特別高。

總結(jié)

這里,再次做一個(gè)知識(shí)點(diǎn)的總結(jié):

  • IoC 是一種概念和設(shè)計(jì)思想,Guice,Spring,Jboss都實(shí)現(xiàn)了這種理念,Spring將其發(fā)揚(yáng)光大

  • Spring IoC 容器實(shí)現(xiàn)基于反射技術(shù)

  • IoC 的3種類型,Spring IOC 支持2種,構(gòu)造函數(shù)注入和屬性注入

  • Spring 容器工廠分為兩種:ApplicationContext、BeanFactory。異同點(diǎn):1. BeanFactory 在啟動(dòng)的時(shí)候不會(huì)去實(shí)例化 Bean,ApplicationContext 在啟動(dòng)的時(shí)候會(huì)。2. BeanFacotry 是 spring 中比較原始的 Factory,無法支持 spring 的許多插件,如 AOP 功能、Web 應(yīng)用等。 ApplicationContext 支持。

  • Bean的生命周期的過程:

    • 四類方法:
      • Bean自身的方法:如Setter init-method 和 destroy-method 所指定的方法;
      • Bean級(jí)生命周期接口方法:如 BeanNameAware、 BeanFactoryAware、 InitializingBean 和 DisposableBean,這些接口方法由 Bean 類直接實(shí)現(xiàn);
      • 容器級(jí)生命周期接口方法: InstantiationAwareBean PostProcessor 和 BeanPostProcessor 這兩個(gè)接口實(shí)現(xiàn),一般稱它們的實(shí)現(xiàn)類為“ 后處理器” 。這些后處理器的影響是全局性的。用戶可以通過合理地編寫后處理器,讓其僅對(duì)感興趣Bean 進(jìn)行加工處理。
      • 工廠后處理器接口方法
  • Bean 的裝配過程:

    • BeanDefinitionReader 讀取 Resource 所指向的配置文件資源,然后解析配置文件。配置文件中每一個(gè)解析成一個(gè) BeanDefinition 對(duì)象,并保存到 BeanDefinitionRegistry 中;
    • 容器掃描 BeanDefinitionRegistry 中的BeanDefinition;調(diào)用InstantiationStrategy 進(jìn)行Bean實(shí)例化的工作;使用 BeanWrapper 完成Bean屬性的設(shè)置工作;
    • 單例Bean緩存池:Spring 在DefaultSingletonBeanRegistry類中提供了一個(gè)用于緩存單實(shí)例 Bean 的緩存器,它是一個(gè)用HashMap實(shí)現(xiàn)的緩存器,單實(shí)例的 Bean 以beanName為鍵保存在這個(gè)HashMap中。
  • bean的裝配方式,四種:1. xml;2. 注解;3. java類的配置;4. 基于Groovy DSL的配置(不太常見)

  • 依賴注入的方式,三種:1. 屬性注入:通過setXxx()方法注入Bean的屬性值;2. 構(gòu)造器注入:通過構(gòu)造函數(shù)注入;3. 工廠方法注入:用靜態(tài)工廠或非靜態(tài)工廠方法注入

  • bean的五個(gè)作用域:1. singleton:單例,只有一個(gè)對(duì)象;2. prototype:多例,每次都會(huì)創(chuàng)建一個(gè)新對(duì)象;3. request:每次請(qǐng)求都會(huì)參數(shù)一個(gè)對(duì)象;4. session:每個(gè)session都會(huì)產(chǎn)生一個(gè)對(duì)象;5. global session:所有的session都用一個(gè)對(duì)象

  • 高級(jí)主題:1. 國際化:多語言;2. 容器事件:監(jiān)聽對(duì)應(yīng)的事件并做出反應(yīng),比如:RequestHandledEvent,當(dāng)一個(gè)Http請(qǐng)求被處理后產(chǎn)生事件;3. 引用外部屬性文件:數(shù)據(jù)庫的用戶名密碼信息存儲(chǔ)在properties文件中,可以使用${XX} 進(jìn)行引用;4. 屬性編輯器:轉(zhuǎn)化為基本類型的利器

最后再附上Spring-Ioc的思維導(dǎo)圖,建議點(diǎn)贊、關(guān)注、收藏,以便隨時(shí)復(fù)習(xí)鞏固知識(shí):

Spring-Ioc的思維導(dǎo)圖

我是帥帥,一個(gè)在互聯(lián)網(wǎng)茍且偷生的工具人。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容