詳解Spring IoC容器

来源:https://www.cnblogs.com/xyfer1018/archive/2020/01/09/12170569.html
-Advertisement-
Play Games

一、Spring IoC容器概述 1.依賴反轉(依賴註入):依賴對象的獲得被反轉了。 如果合作對象的引用或依賴關係的管理由具體對象來完成,會導致代碼的高度耦合和可測試性的降低,這對複雜的面向對象系統的設計是非常不利的。 在Spring中,IoC容器是實現依賴控制反轉這個模式的載體,它可以在對象生成或 ...


一、Spring IoC容器概述

  1.依賴反轉(依賴註入):依賴對象的獲得被反轉了。

  如果合作對象的引用或依賴關係的管理由具體對象來完成,會導致代碼的高度耦合和可測試性的降低,這對複雜的面向對象系統的設計是非常不利的。

  在Spring中,IoC容器是實現依賴控制反轉這個模式的載體,它可以在對象生成或者初始化時直接將數據註入到對象中,也可以通過將對象引用註入到對象數據域中的方式來註入對方法調用的依賴。這種依賴是可以遞歸的,對象被逐層註入。

  關於如何反轉對依賴的控制,把控制權從具體業務對象中轉交到平臺或者框架中,是降低面向對象系統設計複雜性和提高面向對象系統可測試性的一個有效的解決方案。它促進IoC設計模式的發展,是IoC容器要解決的核心問題。

  具體依賴註入的主要實現方式:介面註入(Type 1 IoC)、setter註入(Type 2 IoC)、構造器註入(Type 3 IoC),在Spring的IoC設計中,setter註入和構造器註入是主要的註入方式,相對而言,使用Spring時setter註入是常見的註入方式,而且為了防止註入異常,Spring IoC容器還提供了對特定依賴的檢查。

二、IoC容器系列的設計與實現:BeanFactory和ApplicationContext

  BeanFactory簡單容器系列:這系列容器只實現了容器的最基本功能;

  ApplicationContext高級容器系列:ApplicationContext應用上下文,作為同期的高級形態存在。應用上下文在簡單容器的基礎上,增加了許多面向框架的特性,同時對應用環境做了許多適配。

  IoC容器是用來管理對象依賴關係的,對IoC容器來說,BeanDefinition就是對依賴反轉模式中管理的對象依賴關係的數據抽象,也是容器實現依賴反轉功能的核心數據結構,依賴反轉功能都是圍繞對這個BeanDefinition的處理來完成的。

上圖是IoC容器的介面設計圖,從圖中我們可以看到,IoC容器主要有兩種設計路徑:

1.從介面BeanFactory到HierarchicalBeanFactory,再到ConfigurableBeanFactory,是一條主要的BeanFactory設計路徑。在這條介面設計路徑中,BeanFactory介面定義了基本的IoC容器規範。在這個介面定義中,包括了getBean()這樣的IoC容器的基本方法(通過這個方法可以從容器中取得Bean)。

2.第二條介面設計主線是,以ApplicationContext應用上下文介面為核心的介面設計,這裡涉及的主要介面設計有,從BeanFactory到ListableBeanFactory,再到ApplicationContext,再到我們常用的WebApplicationContext或者ConfigurableApplicationContext介面。對於ApplicationContext介面,它通過繼承MessageSource、ResourceLoader、ApplicationEventPublisher介面,在BeanFactory簡單IoC容器的基礎上添加了許多對高級容器的特性支持。

(一)、BeanFactory

  BeanFactory介面定義了IoC容器最基本的形式,並且提供了IoC容器所應該遵守的最基本的服務契約,同時,這也是我們使用IoC容器所應遵守的最底層和最基本的編程規範,這些介面定義勾出了IoC的基本輪廓。

  BeanFactory和FactoryBean是在Spring中使用頻率很高的類。它們在拼寫上非常相似。一個是Factory,也就是IoC容器或者對象工廠;一個是Bean。在Spring中,所有的Bean都是由BeanFactory(也就是IoC容器)來進行管理的。但對FactoryBean而言,這個Bean不是簡單的Bean,而是一個能產生或者修飾對象生成的工廠Bean,它的實現與設計模式中的工廠模式和修飾器模式類似。

BeanFactory源碼:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.beans.factory;

import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;

public interface BeanFactory {
    String FACTORY_BEAN_PREFIX = "&";

    Object getBean(String var1) throws BeansException;

    <T> T getBean(String var1, @Nullable Class<T> var2) throws BeansException;

    Object getBean(String var1, Object... var2) throws BeansException;

    <T> T getBean(Class<T> var1) throws BeansException;

    <T> T getBean(Class<T> var1, Object... var2) throws BeansException;

    boolean containsBean(String var1);

    boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;

    boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, @Nullable Class<?> var2) throws NoSuchBeanDefinitionException;

    @Nullable
    Class<?> getType(String var1) throws NoSuchBeanDefinitionException;

    String[] getAliases(String var1);
}

通過BeanFactory介面的定義,用戶可以執行以下操作:

  1.通過介面方法getBean獲取Bean,還可以通過參數方法對Bean類型進行檢查;

  2.通過介面方法containsBean讓用戶能夠判斷容器是否含有制定名字的Bean;

  3.通過介面方法isSingleton來查詢指定名字的Bean是否是Singleton類型的Bean。對於Singleton屬性,用戶可以在BeanDefinition中指定;

  4.通過介面方法isPrototype來查詢指定名字的Bean是否是prototype類型的。與Singleton屬性一樣,這個屬性也可以由用戶在BeanDefinition中指定;

  5.通過介面方法isTypeMatch來查詢指定了名字的Bean的Class類型是否是特定的Class類型。這個Class類型可以由用戶指定;

  6.通過介面方法getType來查詢指定名字的Bean的Class類型;

  7.通過介面方法getAliases來查詢指定了名字的Bean的所有別名,這些別名都是用戶在BeanDefinition中定義的;

這些定義的介面方法勾畫出了IoC容器的基本特性。

  為了更清楚地瞭解BeanFactory作為容器的工作原理,我們來看一下BeanFanctory的一個實現類XmlBeanFactory的源代碼:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.beans.factory.xml;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.core.io.Resource;

/** @deprecated */
@Deprecated
public class XmlBeanFactory extends DefaultListableBeanFactory {
    private final XmlBeanDefinitionReader reader;

    public XmlBeanFactory(Resource resource) throws BeansException {
        this(resource, (BeanFactory)null);
    }

    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
        super(parentBeanFactory);
        this.reader = new XmlBeanDefinitionReader(this);
        this.reader.loadBeanDefinitions(resource);
    }
}

  我們看到XmlBeanFactory是用了DefaultListableBeanFactory作為基類,DefaultListableBeanFactory是很重要的一個IoC實現,在其他IoC容器中,比如ApplicationContext,其實現的基本原理和XmlBeanFactory一樣,也是通過持有或者擴展DefaultListableBeanFactory來獲得基本的IoC容器的功能的。

  參考XmlBeanFactory的實現,我們以編程的方式使用DefaultListableBeanFactory。從中我們可以看到IoC容器使用的一些基本過程。

package com.xyfer.controller;

import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.ClassPathResource;

public class IoCDemo {
    public static void main(String[] args) {
        ClassPathResource res = new ClassPathResource("demo.xml");
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
        reader.loadBeanDefinitions(res);
    }
}

  這樣,我們就可以通過factory對象來使用DefaultListableBeanFactory這個IoC容器來。在使用IoC容器時,需要如下幾個步驟:

  1.創建IoC配置文件的抽象資源,這個抽象資源包含了BeanDefinition的定義信息;

  2.創建一個BeanFactory,這裡使用DefaultListableBeanFactory;

  3.創建一個載入BeanDefinition的讀取器,這裡使用XmlBeanDefinitionReader來載入XML文件形式的BeanDefinition,通過一個回調配置給BeanFactory;

  4.從定義好的資源位置讀入配置信息,具體的解析過程由XmlBeanDefinitionReader來完成。完成整個載入和註冊Bean定義之後,需要的IoC容器就建立起來了。這個時候就可以直接使用IoC容器了。

(二)、ApplicationContext

  ApplicationContext除了提供BeanFactory提供的容器的基本功能外,還為用戶提供了以下的附加服務,所以說ApplicationContext是一個高級形態意義的IoC容器。

 從ApplicationContext繼承關係中,可以看到ApplicationContext在BeanFactory的基礎上通過實現不同的介面而添加不同的附加功能。

  1.支持不同的信息源。ApplicationContext擴展了MessageSource介面,這些信息源的擴展功能可以支持國際化的實現,為開發多語言版本的應用提供服務。

  2.訪問資源。這一特性體現在對ResourceLoader和Resource的支持上,這樣我們可以從不同的地方得到Bean定義資源。

  3.支持應用事件。繼承了介面ApplicationEventPublisher,從而在上下文中引入了事件機制。這些事件和Bean的生命周期的結合為Bean的管理提供了便利。

  4.在ApplicationContext中提供的附加服務。這些服務使得基本IoC容器的功能更豐富。一般建議在開發應用時使用ApplicationContext作為IoC容器的基本形式。

三、IoC容器的初始化過程

  簡單來說,IoC容器的初始化是由refresh()方法啟動的,這個方法標誌IoC容器的正式啟動。具體來說,這個啟動包括BeanDefinition的Resource定位、載入和註冊三個基本過程

  1.Resource定位過程。Resource定位指的是BeanDefinition的資源定位,它由ResourceLoader通過統一的Resource介面來完成,這個Resource對各種形式的BeanDefinition的使用都提供來統一的介面。在文件系統中的Bean定義信息可以使用FileSystemResource來進行抽象;在類路徑中的Bean定義信息可以使用ClassPathResource來抽象。

  2.BeanDefinition的載入。這個載入過程是把用戶定義好的Bean表示成IoC容器內部的數據結構,而這個容器內部的數據結構就是BeanDefinition。具體來說,這個BeanDefinition實際上就是POJO對象在IoC容器中的抽象,通過這個BeanDefinition定義的數據結構,使IoC容器能夠方便地對POJO對象也就是Bean進行管理。

  3.向IoC容器註冊這些BeanDefinition的過程。這個過程是通過調用BeanDefinitionRegistry介面的實現來完成的。這個註冊過程把載入過程中解析得到的BeanDefinition向IoC容器進行註冊。通過分析,我們可以看到,在IoC容器內部將BeanDefinition註入到一個HashMap中去,IoC容器就是通過這個HashMap來持用這些BeanDefinition數據的。

  這裡談的是IoC容器初始化過程,這個過程一般不包含Bean依賴註入的實現。在Spring IoC的設計中,Bean定義的載入和依賴註入是兩個獨立的過程。依賴註入一般發生在應用第一次通過getBean向容器索取Bean的時候。但是又一個例外的配置,在使用IoC容器時有一個預實例化的配置,通過這個預實例化的配置(具體來說,可以通過為Bean定義信息中的lazyinit屬性),可以對容器初始化過程做一個微小的控制,從而改變這個被設置了lazyinit屬性的Bean的依賴註入過程。舉例來說,如果我們對某個Bean設置了lazyinit屬性,那麼這個Bean的依賴註入在IoC容器初始化時就預先完成了,而不需要等到整個初始化完成以後,第一次使用getBean時才會觸發。

四、IoC容器的依賴註入

  IoC容器的初始化過程完成的主要工作在IoC容器中建立BeanDefinition數據映射。但是在此過程中IoC容器並沒有對Bean的依賴關係進行註入。

  當IoC容器已經載入了用戶定義的Bean信息,容器中的BeanDefinition數據已經建立好的前提下,依賴註入的過程是在用戶第一次向IoC容器索要Bean時觸發的,也就是第一次調用getBean的時候觸發,當然也有例外,就是當在BeanDefiniton中設置lazyinit屬性來讓容器完成對Bean的預實例化。這個預實例化實際上也是一個完成依賴註入的過程,但是這個依賴註入的過程是在初始化的過程中完成的。

  getBean是依賴註入的起點,之後會調用createBean,Bean對象會依據BeanDefinition定義的要求生成。createBean不但生成了需要的Bean,還對Bean初始化進行了處理,比如實現了在BeanDefinition中的init-method屬性定義,Bean後置處理器等。CGLIB是一個常用的位元組碼生成器的類庫,它提供了一系列的API來提供生成和轉換JAVA的位元組碼的功能。在Spring AOP中也使用CGLIB對JAVA的位元組碼進行增強。在IoC容器中,Spring通過預設類SimpleInstantiationStrategy類來生成Bean對象,它提供了兩種實例化Java對象的方法,一種是通過BeanUtils,它使用了JVM的反射功能,一種是通過CGLIB來生成。

  在實例化Bean對象生成的基礎上,接下來就是各種依賴關係的處理。通過對BeanDefinition中的對象、value值、List、Map等進行解析,然後使用反射對屬性進行註入。

  在Bean的創建和對象依賴註入的過程中,使用遞歸在上下文體系中查找需要的Bean和創建Bean;在依賴註入時,通過遞歸調用容器的getBean方法,得到當前Bean的依賴Bean,同時也觸發對依賴Bean的創建和註入。在對Bean的屬性進行依賴註入時,解析的過程也是遞歸的過程。這樣,根據依賴關係,一層一層地完成Bean的創建和註入,直到最後完成當前Bean的創建。有了這個頂層Bean的創建和對它的屬性依賴註入的完成,意味著和當前Bean相關的整個依賴鏈的註入也完成了。

五、IoC容器的其他相關特性

  1.ApplicationContext和Bean的初始化及銷毀

    Bean的生命周期

    (1)Bean實例的創建

    (2)為Bean實例設置屬性

    (3)調用Bean的初始化方法

    (4)應用可以通過IoC容器使用Bean

    (5)當容器關閉時,調用Bean的銷毀方法

  2.lazy-init屬性和預實例化

  3.FactoryBean的實現

  4.BeanPostProcessor的實現

  5.autowiring(自動依賴裝配)的實現

    配置autowiring屬性,IoC容器會根據這個屬性的配置,使用反射自動查找屬性的類型或者名字,然後基於屬性的類型或名字來自動匹配IoC容器中的Bean,從而自動地完成依賴註入。

  6.Bean的依賴檢查

    Spring通過依賴檢查特性,幫助應用檢查是否所有的屬性都已經被正確設置。在Bean定義時設置dependency-check屬性來指定依賴檢查模式即可。屬性可以設置為none、simple、object、all四種模式,預設的模式是none。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 具體的LinkedList數據結構參考這個鏈接 隊列用法主要來自於Deque,而Deque又繼承自Queue 先從下麵一段代碼說起吧 如果對於隊列的數據結構不瞭解的話,對上面的註釋內容會比較迷糊。Queue介面主要是使LinkedList具有隊列的能力。隊列類似於我們生活中的排隊;特點就是先進先出, ...
  • 一、冒泡排序 1.也就是依次選出最大的放在最後面 package com.bjpowernode.java_learning; ​ public class D70_1_BubbleSort { public static void main(String[] args) { /* * 冒泡排序演算法 ...
  • 本系列筆記主要基於《深入理解Java虛擬機:JVM高級特性與最佳實踐 第2版》,是這本書的讀書筆記。 jstat命令用來查看JVM統計信息,可以查看類載入信息、垃圾收集的信息、JIT編譯信息等等,功能非常豐富。 所有的JDK工具都可以在Oracle官網的 "Java Tools Reference" ...
  • Spring整合單元測試 在前面的案例中我麽需要自己創建ApplicationContext對象,然後在調用getBean來獲取需要測試的Bean Spring提供了一種更加方便的方式來創建測試所需的ApplicationContext,並且可以幫助我們把需要測試的Bean直接註入到測試類中 添加依 ...
  • 固定資產管理系統是對高校固定資產的一個信息化管理系統,基本功能包括:對固定資產的購進、接觸、銷毀,對物品的使用狀態、借出狀態、庫存狀態等進行標識,對各類物品進行編號,根據編號進行查詢,根據名稱進行查詢等。本系統結構如下:(1)系統登錄: 用戶登錄模塊:登錄功能 重置(2)系統用戶管理: 對系統用戶的 ...
  • Redis 是一個開源的使用 ANSI C 語言編寫、支持網路、可基於記憶體亦可持久化的日誌型、Key-Value 資料庫,並提供多種語言的 API。從 2010年 3 月 15 日起,Redis 的開發工作由 VMware 主持。從 2013 年 5 月開始,Redis 的開發由 Pivotal 贊 ...
  • 問題背景 在開發好頁面後,如何讓頁面更快更好的運行,是區分一個程式猿技術水平和視野的一個重要指標。所以面試時,面試官總會問你一個問題,如何進行性能優化呢? 性能優化是什麼 從前端的角度來說,性能優化可以分為兩個方向。從用戶角度來看,一個是頁面載入的很快,另一個是頁面使用起來很流暢。因此,對性能優化的 ...
  • Python真的是無所不能,原因就是因為Python有數目龐大的庫,無數的現成的輪子,讓你做很多很多應用都非常方便。wifi跟我們的生活息息相關,無處不在。今天從WiFi連接的原理,再結合代碼為大家詳細的介紹如何利用python來破解WiFi。、 ! 01.如何連接wifi 首先我們的電腦是如何連接 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...