詳解Spring IoC容器

1、Spring IoC容器概述spring

  1.依賴反轉(依賴注入):依賴對象的得到被反轉了。編程

  若是合做對象的引用或依賴關係的管理由具體對象來完成,會致使代碼的高度耦合和可測試性的下降,這對複雜的面向對象系統的設計是很是不利的。設計模式

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

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

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

2、IoC容器系列的設計與實現:BeanFactory和ApplicationContextthis

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

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

  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容器的基本形式。

3、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時纔會觸發。

4、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相關的整個依賴鏈的注入也完成了。

5、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。

相關文章
相關標籤/搜索