IOC
IOC設計模式是企業應用開發中,解耦元件之間的複雜關系的利器,Spring IOC子產品就是這個模式的一種實作,Spring IOC提供了一個基本的JAVABean容器,通過IOC模式管理依賴關系,并通過依賴注入和AOP切面增強為JAVABean這樣的POJO對象賦予事務管理、生命周期管理等基本功能。
前言:主要記錄IOC初始化過程的幾個步驟,參考:SPRING技術内幕:深入解析SPRING架構與設計原理,多年前翻過的一本Spring原理書,裡面講的細節大部分都忘了,回溯一下,順便做個記錄,也好将來翻閱,對書中的IOC,AOP,WEB等都做詳細的記錄,此章節為Spring系列,會不斷上傳,篇幅原因,一章就講一部分。先簡單介紹一下IOC的幾個底層接口,以及核心容器,主要講解IOC容器初始化的定位部分
涉及核心接口
Resource
資料源接口,封裝了InputStream,作用是為容器提供使用者資料源的接口。通俗的講,容器啟動需要的配置檔案或配置類在哪?
ResourceLoader
加載資源的政策接口
BeanDefinition
BeanDefinition是IOC容器體系中非常重要的核心資料結構,可以看成是對bean定義的抽象
BeanDefinitionReader
用于将Resource裝載入BeanDedintion中的解析器
BeanFactory
容器基類接口,所有IOC容器的父接口
ApplicationContext
應用上下文容器的父接口
紅色線路從接口BeanFactory——HierarchicalBeanFactroy——ConfigurableBeanFactory主要設計路徑
以ApplicationContext應用上下文接口為核心的接口設計,主要涉及幾口
BeanFactory——ListableBeanFactory——ApplicationContext——WebApplicationContext / ConfigurableApplicationContext
常用的應用上下文基本上都是WebApplicationContext / ConfigurableApplicationContext實作在這個接口體系中
ListableBeanFactory和HierarchicalBeanFactory兩個接口,連接配接BeanFactory接口定義和ApplicationContext應用上下文的接口定義。
ApplicationContext通過繼承MessageSouree、ResourceLoader、ApplicationEvenPublisher接口,在BeanFactory簡單容器的基礎上添加了許多對進階容易的特性支援
IOC初始化過程
定位
Resource定位過程,這裡指的是BeanDefinition的資源定位,它由ResourceLoader通過統一的Resource接口完成,這個Resource對各種形式的BeanDefinition的使用提供了統一接口,如FileSystemResource、ClassPathResource。定位過程類似于容器尋找資料的過程,就像水桶裝水先要找水。
BeanDefinition的Resource定位步驟:
以程式設計的方式使用DefaultListableBeanFactory,先要定義Resource來定位容器使用的BeanDefinition.
ClassPathResource resource=new ClassPathResource("beans.xml"); (找水)建立IOC配置檔案的資源,
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();(桶)建立一個BeanFactory。
(裝水器)建立一個讀取器,resource并不能直接被使用,必須通過BeanDefinitionReader來對這些資訊進行處理。
XmlBeanDefinitionReader reader=new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinition(res);(裝水)從定義好的資源位置讀入配置資訊
DefaultListableBeanFactory
是接口ConfigurableListableBeanFactory和BeanDefinitionRegistry的預設實作,是一個純粹的容器,需要為它配置特定的讀取器才能完成這些功能,适合用于定制IOC,可以了解為Spring容器中預設的對象工廠的實作,是一個比較全面的對象工廠。工廠中的Bean都是基于中繼資料定義的 bean 和通過post-processors擴充的bean。它的典型作用就是注冊所有的bean通過讀取bean的配置檔案,這樣就可以通過bean的命名友善快速通路bean,通過本地緩存表。
ApplicationContext
一般我們使用ApplicationContext容器,ApplicationContext通過繼承MessageSouree、ResourceLoader、ApplicationEvenPublisher接口,在BeanFactory簡單容器的基礎上添加了許多對進階容易的特性支援,這個接口系統是以BeanFactory和ApplicationContext為核心的,而BeanFactory又是IOC容器的最基本接口,在ApplicationContext的設計中,一方面,可以看到它內建了BeanFactory接口體系中的ListableBeanFactory、autowireCapableBeanFactory\HierarchicalBeanFactory等BeanFactory接口,具備了BeanFactory IOC容器的基本功能,另一方面通過繼承MessageSource\ResourceLoader\ApplicationEventPublisher這些幾口,BeanFactory為ApplicationContext賦予了更進階的IOC容器特性,為了再Web環境中使用它,還涉及了WebApplicationContext接口,而這個接口通過繼承ThemeSource接口來擴充功能
先來看看三個常用的ApplicationContext實作類:
FileSystemXmlApplicationContext:從XML檔案中載入Resource(配置檔案)
ClassPathXmlApplicationContext:從類路徑載入Resource(配置類)
XmlWebApplicationContext:從Web容器載入Resource(web項目)
我們主要以FileSystemXmlApplicationContext為例,講述一下IOC容器中ApplicationContext系列的定位過程
先來看看FileSystemXmlApplicationContext的繼承體系
FileSystemXmlApplicationContext的具體實作,要走進源碼,篇幅有限,隻貼關鍵代碼
//初始化過程中,調用refresh函數載入BeanDefinition,這個refresh啟動了BeanDefinition的載入過程,它是容器啟動的入口
public FileSystemXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
@Override
protected Resource getResourceByPath(String path) {
if (path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
我們來找找BeanDefinition的完整定位過程,首先檢視調用棧
清除看到BeanDefinition資源定位的過程,最初由refresh方法觸發
那麼,FileSystemXmlApplicationContext是在哪裡BeanDefinitionReader的呢?
根據調用棧進入源碼
AbstractApplicationContext——refresh()方法
較長的描述了整個ApplicationContext的初始化過程,BeanFactory更新,MessageSource和PostProcessor注冊,看起來更像是對ApplicationContext進行初始化的模闆或執行提綱,為Bean的生命周期管理提供了條件
進入obtainFreshBeanFactory()——調用了模闆方法refreshBeanFactory();該方法由子類去實作,這裡的實作類是 AbstractRefreshableApplicationContext類
loadBeanDefinitions()方法是AbstractRefreshableApplicationContext類的抽象方法,由子類 AbstractXmlApplicationContext來實作
執行個體化了XmlBeanDefinitionReader
具體實作:loadBeanDefinitions(XmlBeanDefinitionReader reader)
看到具體的載入過程是委托給BeanDefinitionReader來完成的,因為這裡的BranDefinition是通過XML檔案定義,是以這裡使用XmlBeanDefinitionReader 來載入BeanDefinition到容器中
我們得出部分結論
1: FileSystemXmlApplicationContext通過調用refresh()容器啟動方法 其中實作了BeanDefinition的載入,
2:載入過程由XmlBeanDefinitionReader來完成,由于使用的是XML方式的定義,是以使用的是 XmlBeanDefinitionReader、如果使用了其他的BeanDefinition定義,則需要其他的BeanDefinitionReader實作類來完成
3:容器實際使用的IOC容器是标準容器DefultListableBeanFactory
而Resource載入在BeanDefinitionReader讀入BeanDefinition時實作
進入reader.loadBeanDefinitions(configLocations);
loadBeanDefinitions(location)最後會通過resourceLoader.getResources(location)獲得resource
再調用loadBeanDefinitions(resource);
其中getResources(location)為抽象方法,使用的類是DefaultResourceLoader()
DefaultResourceLoader預設使用ClassPathContextResource來獲得resource
再看看FileSystemXmlApplicationContext的內建路線上層繼承了DefaultResourceLoader,重寫了getResourceByPath(String path)方法,使用FileSystemResource傳回Resource,在參數Resource中封裝了對XML檔案的I/O操作,是以讀取器可以在打開I/O流後得到XML的檔案對象。有了這個檔案對象以後,将其解析為Document對象,再按照Spring的Bean定義規則來對XML的文檔樹進行解析,這個解析交給了BeanDefinitionParserDelegate來完成。
到此,ApplicationContext的resource定位流程就結束了,而後面的解析,注冊,注入将在下一篇中具體講解