Spring:原始碼解讀Spring IOC原理

郭慕榮發表於2021-12-31

一、什麼是Ioc/DI?
IoC 容器:最主要是完成了完成物件的建立和依賴的管理注入等等。先從我們自己設計這樣一個視角來考慮:所謂控制反轉,就是把原先我們程式碼裡面需要實現的物件建立、依賴的程式碼,反轉給容器來幫忙實現。那麼必然的我們需要建立一個容器,同時需要一種描述來讓容器知道需要建立的物件與物件的關係。這個描述最具體表現就是我們可配置的檔案。物件和物件關係怎麼表示?可以用 xml , properties 檔案等語義化配置檔案表示。描述物件關係的檔案存放在哪裡?可能是 classpath , filesystem ,或者是 URL 網路資源, servletContext 等。回到正題,有了配置檔案,還需要對配置檔案解析。不同的配置檔案對物件的描述不一樣,如標準的,自定義宣告式的,如何統一? 在內部需要有一個統一的關於物件的定義,所有外部的描述都必須轉化成統一的描述定義。
如何對不同的配置檔案進行解析?需要對不同的配置檔案語法,採用不同的解析器
二、 Spring IOC體系結構?
(1) BeanFactory
Spring Bean的建立是典型的工廠模式,這一系列的Bean工廠,也即IOC容器為開發者管理物件間的依賴關係提供了很多便利和基礎服務,在Spring中有許多的IOC容器的實現供使用者選擇和使用,其相互關係如下:

其中BeanFactory作為最頂層的一個介面類,它定義了IOC容器的基本功能規範,BeanFactory 有三個子類:ListableBeanFactory、HierarchicalBeanFactory 和AutowireCapableBeanFactory。但是從上圖中我們可以發現最終的預設實現類是 DefaultListableBeanFactory,他實現了所有的介面。那為何要定義這麼多層次的介面呢?查閱這些介面的原始碼和說明發現,每個介面都有他使用的場合,它主要是為了區分在 Spring 內部在操作過程中物件的傳遞和轉化過程中,對物件的資料訪問所做的限制。例如 ListableBeanFactory 介面表示這些 Bean 是可列表的,而 HierarchicalBeanFactory 表示的是這些 Bean 是有繼承關係的,也就是每個Bean 有可能有父 Bean。AutowireCapableBeanFactory 介面定義 Bean 的自動裝配規則。這四個介面共同定義了 Bean 的集合、Bean 之間的關係、以及 Bean 行為。最基本的IOC容器介面BeanFactory

public interface BeanFactory {    
     
     //對FactoryBean的轉義定義,因為如果使用bean的名字檢索FactoryBean得到的物件是工廠生成的物件,    
     //如果需要得到工廠本身,需要轉義           
     String FACTORY_BEAN_PREFIX = "&"; 
        
     //根據bean的名字,獲取在IOC容器中得到bean例項    
     Object getBean(String name) throws BeansException;    
   
     //根據bean的名字和Class型別來得到bean例項,增加了型別安全驗證機制。    
      Object getBean(String name, Class requiredType) throws BeansException;    
     
     //提供對bean的檢索,看看是否在IOC容器有這個名字的bean    
      boolean containsBean(String name);    
     
     //根據bean名字得到bean例項,並同時判斷這個bean是不是單例    
     boolean isSingleton(String name) throws NoSuchBeanDefinitionException;    
     
     //得到bean例項的Class型別    
     Class getType(String name) throws NoSuchBeanDefinitionException;    
     
     //得到bean的別名,如果根據別名檢索,那麼其原名也會被檢索出來    
    String[] getAliases(String name);    
     
 }

在BeanFactory裡只對IOC容器的基本行為作了定義,根本不關心你的bean是如何定義怎樣載入的。正如我們只關心工廠裡得到什麼的產品物件,至於工廠是怎麼生產這些物件的,這個基本的介面不關心。而要知道工廠是如何產生物件的,我們需要看具體的IOC容器實現,spring提供了許多IOC容器的實現。比如XmlBeanFactory,ClasspathXmlApplicationContext等。其中XmlBeanFactory就是針對最基本的ioc容器的實現,這個IOC容器可以讀取XML檔案定義的BeanDefinition(XML檔案中對bean的描述),如果說XmlBeanFactory是容器中的屌絲,ApplicationContext應該算容器中的高帥富。ApplicationContext是Spring提供的一個高階的IoC容器,它除了能夠提供IoC容器的基本功能外,還為使用者提供了以下的附加服務。從ApplicationContext介面的實現,我們看出其特點:

  • 1.  支援資訊源,可以實現國際化。(實現MessageSource介面)
  • 2.  訪問資源。(實現ResourcePatternResolver介面,這個後面要講)
  • 3.  支援應用事件。(實現ApplicationEventPublisher介面)

(2) BeanDefinition

SpringIOC容器管理了我們定義的各種Bean物件及其相互的關係,Bean物件在Spring實現中是以BeanDefinition來描述的,其繼承體系如下:

Bean 的解析過程非常複雜,功能被分的很細,因為這裡需要被擴充套件的地方很多,必須保證有足夠的靈活性,以應對可能的變化。Bean 的解析主要就是對 Spring 配置檔案的解析。這個解析過程主要通過下圖中的類完成:

三、IoC容器的初始化?

IoC容器的初始化包括BeanDefinition的Resource定位、載入和註冊這三個基本的過程。我們以ApplicationContext為例講解,ApplicationContext系列容器也許是我們最熟悉的,因為web專案中使用的XmlWebApplicationContext就屬於這個繼承體系,還有ClasspathXmlApplicationContext等,其繼承體系如下圖所示:

 

ApplicationContext允許上下文巢狀,通過保持父上下文可以維持一個上下文體系。對於bean的查詢可以在這個上下文體系中發生,首先檢查當前上下文,其次是父上下文,逐級向上,這樣為不同的Spring應用提供了一個共享的bean定義環境。
下面我們分別簡單地演示一下兩種ioc容器的建立過程
1、XmlBeanFactory(屌絲IOC)的整個流程
通過XmlBeanFactory的原始碼,我們可以發現:

public class XmlBeanFactory extends DefaultListableBeanFactory{

     private final XmlBeanDefinitionReader reader; 
 
     public XmlBeanFactory(Resource resource)throws BeansException{
         this(resource, null);
     }
     
     public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory)
          throws BeansException{
         super(parentBeanFactory);
         this.reader = new XmlBeanDefinitionReader(this);
         this.reader.loadBeanDefinitions(resource);
    }
 }
//根據Xml配置檔案建立Resource資源物件,該物件中包含了BeanDefinition的資訊
 ClassPathResource resource =new ClassPathResource("application-context.xml");
//建立DefaultListableBeanFactory
 DefaultListableBeanFactory factory =new DefaultListableBeanFactory();
//建立XmlBeanDefinitionReader讀取器,用於載入BeanDefinition。之所以需要BeanFactory作為引數,是因為會將讀取的資訊回撥配置給factory
 XmlBeanDefinitionReader reader =new XmlBeanDefinitionReader(factory);
//XmlBeanDefinitionReader執行載入BeanDefinition的方法,最後會完成Bean的載入和註冊。完成後Bean就成功的放置到IOC容器當中,以後我們就可以從中取得Bean來使用
 reader.loadBeanDefinitions(resource);

參考部落格:https://www.cnblogs.com/ITtangtang/p/3978349.html#a1   https://zhuanlan.zhihu.com/p/32830470

相關文章