天天看點

Spring(2)——Spring IoC 詳解

Spring IoC 概述

IoC:Inverse of Control(控制反轉)

  • 讀作“反轉控制”,更好了解,不是什麼技術,而是一種設計思想,就是将原本在程式中手動建立對象的控制權,交由Spring架構來管理。
  • 正控:若要使用某個對象,需要自己去負責對象的建立
  • 反控:若要使用某個對象,隻需要從 Spring 容器中擷取需要使用的對象,不關心對象的建立過程,也就是把建立對象的控制權反轉給了Spring架構
  • 好萊塢法則:Don’t call me ,I’ll call you

一個例子

控制反轉顯然是一個抽象的概念,我們舉一個鮮明的例子來說明。

在現實生活中,人們要用到一樣東西的時候,第一反應就是去找到這件東西,比如想喝新鮮橙汁,在沒有飲品店的日子裡,最直覺的做法就是:買果汁機、買橙子,然後準備開水。值得注意的是:這些都是你自己“主動”創造的過程,也就是說一杯橙汁需要你自己創造。

然而到了今時今日,由于飲品店的盛行,當我們想喝橙汁時,第一想法就轉換成了找到飲品店的聯系方式,通過電話等管道描述你的需要、位址、聯系方式等,下訂單等待,過一會兒就會有人送來橙汁了。

請注意你并沒有“主動”去創造橙汁,橙汁是由飲品店創造的,而不是你,然而也完全達到了你的要求,甚至比你創造的要好上那麼一些。

Spring IoC 闡述

這就是一種控制反轉的理念,上述的例子已經很好的說明了問題,我們再來描述一下控制反轉的概念:控制反轉是一種通過描述(在 Java 中可以是 XML 或者注解)并通過第三方(Spring)去産生或擷取特定對象的方式。

  • 好處:

    降低對象之間的耦合

    我們不需要了解一個類的具體實作,隻需要知道它有什麼用就好了(直接向 IoC 容器拿)

主動建立的模式中,責任歸于開發者,而在被動的模式下,責任歸于 IoC 容器,基于這樣的被動形式,我們就說對象被控制反轉了。(也可以說是反轉了控制)

Spring IoC 容器

Spring 會提供 IoC 容器來管理和容納我們所開發的各種各樣的 Bean,并且我們可以從中擷取各種釋出在 Spring IoC 容器裡的 Bean,并且通過描述可以得到它。

Spring IoC 容器的設計

Spring IoC 容器的設計主要是基于以下兩個接口:

  • BeanFactory
  • ApplicationContext

其中 ApplicationContext 是 BeanFactory 的子接口之一,換句話說:BeanFactory 是 Spring IoC 容器所定義的最底層接口,而 ApplicationContext 是其最進階接口之一,并對 BeanFactory 功能做了許多的擴充,是以在絕大部分的工作場景下,都會使用 ApplicationContext 作為 Spring IoC 容器。

從上圖中我們可以幾乎看到, BeanFactory 位于設計的最底層,它提供了 Spring IoC 最底層的設計,為此,我們先來看看該類中提供了哪些方法:

由于這個接口的重要性,是以有必要在這裡作一下簡短的說明:

  • 【getBean】 對應了多個方法來擷取配置給 Spring IoC 容器的 Bean。

    ① 按照類型拿 bean:

    bean = (Bean) factory.getBean(Bean.class);

    注意:要求在 Spring 中隻配置了一個這種類型的執行個體,否則報錯。(如果有多個那 Spring 就懵了,不知道該擷取哪一個)

    ② 按照 bean 的名字拿 bean:

    bean = (Bean) factory.getBean("beanName");

    注意:這種方法不太安全,IDE 不會檢查其安全性(關聯性)

    ③ 按照名字和類型拿 bean:(推薦)

    bean = (Bean) factory.getBean("beanName", Bean.class);

  • 【isSingleton】 用于判斷是否單例,如果判斷為真,其意思是該 Bean 在容器中是作為一個唯一單例存在的。而【isPrototype】則相反,如果判斷為真,意思是當你從容器中擷取 Bean,容器就為你生成一個新的執行個體。

    注意:在預設情況下,【isSingleton】為 ture,而【isPrototype】為 false

  • 關于 type 的比對,這是一個按 Java 類型比對的方式
  • 【getAliases】方法是擷取别名的方法

這就是 Spring IoC 最底層的設計,所有關于 Spring IoC 的容器将會遵守它所定義的方法。

根據 ApplicationContext 的類繼承關系圖,可以看到 ApplicationContext 接口擴充了許許多多的接口,是以它的功能十分強大,是以在實際應用中常常會使用到的是 ApplicationContext 接口,因為 BeanFactory 的方法和功能較少,而 ApplicationContext 的方法和功能較多。

通過上一篇 IoC 的例子,我們來認識一個 ApplicationContext 的子類——ClassPathXmlApplicationContext。

  1. 先在【src】目錄下建立一個 【bean.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 通過 xml 方式裝配 bean -->
    <bean name="source" class="pojo.Source">
        <property name="fruit" value="橙子"/>
        <property name="sugar" value="多糖"/>
        <property name="size" value="超大杯"/>
    </bean>
</beans>
           
  1. 這裡定義了一個 bean ,這樣 Spring IoC 容器在初始化的時候就能找到它們,然後使用 ClassPathXmlApplicationContext 容器就可以将其初始化:
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Source source = (Source) context.getBean("source", Source.class);

System.out.println(source.getFruit());
System.out.println(source.getSugar());
System.out.println(source.getSize());
           

這樣就會使用 Application 的實作類 ClassPathXmlApplicationContext 去初始化 Spring IoC 容器,然後開發者就可以通過 IoC 容器來擷取資源了啦!

關于 Spring Bean 的裝配以及一些細節,會在下一篇文章中講到

ApplicationContext 常見實作類:

1.ClassPathXmlApplicationContext:

讀取classpath中的資源

ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
           

2:FileSystemXmlApplicationContext:-

讀取指定路徑的資源

ApplicationContext ac = new FileSystemXmlApplicationContext("c:/applicationContext.xml");
           

3.XmlWebApplicationContext:

需要在Web的環境下才可以運作

XmlWebApplicationContext ac = new XmlWebApplicationContext(); // 這時并沒有初始化容器
ac.setServletContext(servletContext); // 需要指定ServletContext對象
ac.setConfigLocation("/WEB-INF/applicationContext.xml"); // 指定配置檔案路徑,開頭的斜線表示Web應用的根目錄
ac.refresh(); // 初始化容器
           

BeanFactory 和 ApplicationContext 的差別:

  • BeanFactory:是Spring中最底層的接口,隻提供了最簡單的IoC功能,負責配置,建立和管理bean。

    在應用中,一般不使用 BeanFactory,而推薦使用ApplicationContext(應用上下文),原因如下。

  • ApplicationContext:

    1.繼承了 BeanFactory,擁有了基本的 IoC 功能;

    2.除此之外,ApplicationContext 還提供了以下功能:

    ① 支援國際化;

    ② 支援消息機制;

    ③ 支援統一的資源加載;

    ④ 支援AOP功能;

Spring IoC 的容器的初始化和依賴注入

雖然 Spring IoC 容器的生成十分的複雜,但是大體了解一下 Spring IoC 初始化的過程還是必要的。這對于了解 Spring 的一系列行為是很有幫助的。

注意:Bean 的定義和初始化在 Spring IoC 容器是兩大步驟,它是先定義,然後初始化和依賴注入的。

  • Bean 的定義分為 3 步:

    1.Resource 定位

    Spring IoC 容器先根據開發者的配置,進行資源的定位,在 Spring 的開發中,通過 XML 或者注解都是十分常見的方式,定位的内容是由開發者提供的。

    2.BeanDefinition 的載入

    這個時候隻是将 Resource 定位到的資訊,儲存到 Bean 定義(BeanDefinition)中,此時并不會建立 Bean 的執行個體

    3.BeanDefinition 的注冊

    這個過程就是将 BeanDefinition 的資訊釋出到 Spring IoC 容器中

    注意:此時仍然沒有對應的 Bean 的執行個體。

做完了以上 3 步,Bean 就在 Spring IoC 容器中被定義了,而沒有被初始化,更沒有完成依賴注入,也就是沒有注入其配置的資源給 Bean,那麼它還不能完全使用。

對于初始化和依賴注入,Spring Bean 還有一個配置選項——【lazy-init】,其含義就是是否初始化 Spring Bean。在沒有任何配置的情況下,它的預設值為 default,實際值為 false,也就是 Spring IoC 預設會自動初始化 Bean。如果将其設定為 true,那麼隻有當我們使用 Spring IoC 容器的 getBean 方法擷取它時,它才會進行 Bean 的初始化,完成依賴注入。

IoC 是如何實作的

最後我們簡單說說IoC是如何實作的。想象一下如果我們自己來實作這個依賴注入的功能,我們怎麼來做? 無外乎:

  1. 讀取标注或者配置檔案,看看JuiceMaker依賴的是哪個Source,拿到類名
  2. 使用反射的API,基于類名執行個體化對應的對象執行個體
  3. 将對象執行個體,通過構造函數或者 setter,傳遞給 JuiceMaker

我們發現其實自己來實作也不是很難,Spring實際也就是這麼做的。這麼看的話其實IoC就是一個工廠模式的更新版!當然要做一個成熟的IoC架構,還是非常多細緻的工作要做,Spring不僅提供了一個已經成為業界标準的Java IoC架構,還提供了更多強大的功能,是以大家就别去造輪子啦!希望了解IoC更多實作細節不妨通過學習Spring的源碼來加深了解!

引用位址:這裡

【參考資料】:《Java EE 網際網路輕量級架構整合開發》、《Spring 實戰(第四版)》

【好文推薦】:①Spring 的本質系列(1) -- 依賴注入、 ②Spring的IoC原理

歡迎轉載,轉載請注明出處!

簡書ID:@我沒有三顆心髒

github:wmyskxz

歡迎關注公衆微信号:wmyskxz_javaweb

分享自己的Java Web學習之路以及各種Java學習資料

繼續閱讀