Spring-5.1.5源碼解析【IOC】(一)

不管是自己做項目煤篙,還是工作中,Spring都是使用最多的框架猎荠。但是一直都沒有好好的了解一下,本篇博客的目的就是為了深入分析Spring最核心的概念之一:IOC容器的實現(xiàn)原理

通常我們在獲取Bean的時候炮车,會這么去做:

//創(chuàng)建Spring容器

?ApplicationContext ctx=new?ClassPathXmlApplicationContext("classpath*:beans.xml");??

//通過getBean()方法獲取Bean實例??

Person?person=(Person)?ctx.getBean("person");??

我們來看看這個ClassPathXmlApplicationContext里面是怎么樣的

下圖是ClassPathXmlApplicationContext的繼承體系:

下面我們就深入的來看看new ClassPathXmlApplicationContext()是做了什么:

可以發(fā)現(xiàn)浊仆,不管我們是怎么創(chuàng)建ClassPathXmlApplicationContext對象,它內(nèi)部其實都是調(diào)用同一個構(gòu)造器的芳杏,我們來看看他最后調(diào)用的那個構(gòu)造器是怎么樣的


一步步看矩屁,先看看super(parent);是在干什么:


寫不下了爵赵,還是直接寫出來吧

? 1. super(parent):

結(jié)尾是在AbstractApplicationContext的構(gòu)造方法中吝秕,來看看這個構(gòu)造方法


?1.1 this():

? ? ? ? ? ? ? ? ? 首先是調(diào)用了上面的那個構(gòu)造器,我們先來看看this.resourcePatternResolover是什么飛機(jī)

用于將位置模式(例如空幻,Ant-style 的路徑模式)解析為資源對象的策略接口(稍后我們就會看到)烁峭,在這個類中還提到了 :此接口還建議對類路徑中的所有匹配資源使用新的資源前綴“classpath*:”。注意,在這種情況下约郁,資源位置應(yīng)該是沒有占位符的路徑(例如“/beans.xml”)缩挑;JAR文件或類目錄可以包含多個同名文件。

也就是this的目的其實就是為了給這個resourcePatternResolver變量賦值鬓梅,我們來看看具體是怎么做的:

方法上加了注釋供置,這樣看起來可能會更加方便一點.....

這里是直接new PathMatchingResourcePatternResolver(this); 話不多說繼續(xù)往下點

解釋一下這里:這里是把AbstractApplicationContext給設(shè)置進(jìn)PathMatchingResourcePatternResolver中(AbstractAppplicationContext是DefaultResourceLoader的子類,而DefaultResourceLoader是ResourceLoader的實現(xiàn)類)

PathMatchingResourcePatternResolver :


一個ResourcePatternResolver 能夠解析指定資源位置到一個或者多個匹配資源的路徑绽快,源路徑可以是一個簡單的路經(jīng)芥丧,它具有到目標(biāo)為Resource,或者可能包含特殊的 classpath*: 前綴和 / 或者內(nèi)部Ant-style正則表達(dá)式

沒有通配符:

在簡單情況下坊罢,如果指定的位置路經(jīng)不是以 “classpath*:”為前綴续担,并且不包含PathMatcher模式,這個解析器將通過getResource()調(diào)用基礎(chǔ)ResourceLoader艘绍。例如 file:C:/context.xml赤拒,偽URL 例如: classpath:/context.xml,以及簡單的不固定路經(jīng) /WEB-INF/context.xml

Ant-style模式:

/WEB-INF/*-context.xml

com/mycompany/*applicationContext.xml

file:C:/some/path/*-context.xml

classpath:com/mycompany/*applicationContext.xml


http://sishuok.com/forum/blogPost/list/0/2458.html#7106 大家可以看這篇博客了解更多

第一步是設(shè)置給parent這個成員變量诱鞠,這里就不用說了挎挖,來看下Environment是個什么類吧

Environment :

表示當(dāng)前應(yīng)用程序運行環(huán)境的接口,為應(yīng)用程序環(huán)境的兩個關(guān)鍵方面建模 profiles 和 properties航夺,與屬性訪問相關(guān)的方法通過PropertyResolver 超級接口蕉朵。

profile :是要注冊的命名的邏輯Bean定義組,只有當(dāng)給定的配置文件處于活動狀態(tài)時阳掐,才使用容器始衅,可以分配bean到一個配置文件,無論是Xml定義的還是通過注解定義的 或者 @profile 注解缭保,與配置文件相關(guān)的Environment對象的角色是確認(rèn)哪些配置文件(如果有) 當(dāng)前是getActiveProfiles 汛闸,如果沒有則 getDefaultProfiles,這里大家應(yīng)該都有用到過applicationContext-dev.xml艺骂,applicationContext-prod.xml 這種選擇測試跟線上環(huán)境的方法诸老,就是在配置文件中添加

Properties:幾乎在所有應(yīng)用程序中發(fā)揮重要作用,并且可能源于各種源: 屬性文件钳恕,JVM系統(tǒng)屬性别伏,系統(tǒng)環(huán)境變量,JNDI忧额,servlet上下文參數(shù)厘肮,特殊屬性對象,Maps 等等.與屬性相關(guān)的環(huán)境對象的角色是為用戶提供方便的環(huán)境接口睦番,用于配置屬性源并且從中解析屬性

ApplicationContext 內(nèi)管理的bean可注冊為 EnvironmentAware 或者 Environment 以便直接查詢配置文件狀態(tài)或解析屬性类茂。

然而在大多情況下,application級別的bean不需要與Environment交互,但是必須有 ${...} 屬性 由屬性占位符配置程序PropertySourcesPlaceholderConfigurer巩检,其本身就是EnvironmentAware,從Spring 3.1開始恬涧,在使用時默認(rèn)注冊? <context:property-placeholder/>

環(huán)境對象的配置必須通過 ConfigurableEnvironment接口,返回來自所有AbstractApplicationContext子類 getEnvironment 方法

反正這個玩意看圖就知道了


還有一些方法沒寫出來碴巾,懶得寫了?https://jinnianshilongnian.iteye.com/blog/2000183 這個里面講的比較詳細(xì)

到這里溯捆,setSuper(parent);這個方法就算是完了厦瓢√嶙幔總結(jié)一下:

? ??????1.在PathMatchingResourcePatternResolver中設(shè)置AbstractApplicationContext為resourceLoader

????????2.AbstractApplicationContext設(shè)置ResourcePatternResolver

? ? ? ? 3.?AbstractApplicationContext?設(shè)置ApplicationContext

先不要管為啥要這樣子設(shè)置,看到后面就知道了....




2. setConfigLocations(configLocations):

? ? ? ? ? ? ? ? 先不著急說什么煮仇,先把前面幾個方法給貼上劳跃,反正也沒幾個方法

String CONFIG_LOCATION_DELIMITERS = ",; \t\n";
resolverPath() 這個方法才是具體的方法


看到這個getEnvironment()應(yīng)該可以聯(lián)想到剛才看的吧
調(diào)用的是StandardEnvironment中的resolveRequiredPlaceholders方法,最后在AbstractEnvironment中找到浙垫,該類為StandardEnvironment的父類


繼續(xù)往下點


再往下點


private PropertyPlaceholderHelper strictHelper;
這里是直接就給new了一個PropertyPlaceholderHelper對象



在這個地方我們是直接跳過來的刨仑,所以大家直接按照順序看圖就可以了,在之氣那我們調(diào)用helper.replacePlaceholders(),傳的參數(shù)是?text, this::getPropertyAsRawString夹姥,我們找到這個getPropertyAsRawString方法杉武,這個方法是一個抽象類,那么我們直接看這個類(AbstractPropertyResolver)的子類辙售,按照順序我們找到子類轻抱,并且看它的實現(xiàn)

在我們通過AbstractEnvironmentt的resolveRequiredPlaceholders方法再進(jìn)入到AbstractPropertyResolver中的過程這里再截圖說明一下:

當(dāng)時是直接新建new了一個StandardEnvironment過來的,可以往上看看回憶一下


這個地方就比較有意思了

將MutablePropertySources 設(shè)置進(jìn)PropertySourcesPropertyResolver的propertySources屬性


而且注意到?jīng)]有旦部,這個PropertySourcesPropertyResolver就是AbstractPropertyResolver 的子類祈搜,我們剛才就是在找這個王八蛋,接下來我們走下斷點


接著往下走
這個propertySources不為null那是肯定的士八,因為我們看到在AbstaractEnvironment中已經(jīng)給他么的new了一個MutablePropertySources了
然后我們再來看看為什么會有值容燕,還特么可以迭代

這個地方咱們還得回到之前的地方看看

這里它給咱們new了一個StandardEnvironment對象,咱們點進(jìn)去
因為隱式super婚度,這里走父類蘸秘,然后父類中剛好把這個MutablePropertySources傳進(jìn)去,傳進(jìn)去就是為了添加這兩個屬性
添加這兩個屬性就是為了獲取系統(tǒng)環(huán)境變量跟屬性


總結(jié):

1.設(shè)置AbstractApplicationContext中的?ConfigurableEnvironment environment陕见;

2.解析我們ClassPathXmlApplicationContext傳入的資源中的占位符


3秘血、refresh()

好了味抖,終于到了最后的refresh方法了

截不全只能這樣了评甜。 ==?

先從第一個方法開始看吧 ==!

1仔涩、prepareRefresh()

時間還長忍坷,方法還多,慢慢來,一個個來看

首先是設(shè)置AbstractApplicationContext的一些初始化變量

initPropertySources();????????????????????????????在上下文環(huán)境中初始化任何占位符屬性源佩研,留給子類拓展的

這個方法主要是留給子類或者我們來自己拓展的柑肴,其實就是往AbstractPropertyResolver的 requiredProperties集合中添加元素,這個集合是用來存儲初始化時旬薯,必須存在的環(huán)境變量

接下里我們就來手動實驗一下這個方法該怎么來玩:

定義一個類實現(xiàn)ClassPathXmlApplication晰骑,然后重寫AbstractApplicationContext的initPropertySources方法,最重要的調(diào)用父類構(gòu)造方法可別把參數(shù)給漏了? ?==, 然后我們在initPropertySources就調(diào)用的是setRequiredProperties中設(shè)置了一個 "HALOSKY_HOME" (這里就不再說為什么要getEnvironment了绊序,前面應(yīng)該說了兩遍吧)

然后我們貼上測試圖:

這個地方原理就是這樣硕舆,在initPropertySources 方法中通過AbstractPropertyResolver的setRequiredProperties方法往requiredProperties集合中添加必不可缺的環(huán)境變量
我們也可以通過添加系統(tǒng)環(huán)境變量的方式來做,因為在上面的截圖我們可以很清楚的發(fā)現(xiàn)骤公,其實在啟動時是會把系統(tǒng)中的環(huán)境變量給讀取進(jìn)來的


看吧抚官,沒問題~
這里就不用說了吧...看到后面就能引出來 ==


2. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()

在Spring源碼深度解析這本書中是這么介紹以下這幾個類的:

? ? 1. BeanFactory: 定義獲取bean及bean的各種屬性

? ? 2. HierarchicalBeanFactory: 繼承BeanFactory,也就是BeanFactory定義的功能的基礎(chǔ)上增加了對? ? ? ? ? ? ? ? ? ? ? ????????parentFactory的支持阶捆。

? ? 3. ListableBeanFactory: 根據(jù)各種條件獲取bean的配置清單

? ? 4. ConfigurableBeanFactory: 根據(jù)配置Factory的各種方法

? ? 5. ConfigurableListableBeanFactory: beanFactroy配置清單凌节,指定忽略類型以及接口等。

? ? 6. DefaultListableBeanFactory:綜合上面的所有功能洒试,主要是對bean注冊后的處理.

XmlBeanFactory對DefaultListableBeanFactory類進(jìn)行了拓展倍奢,主要是用以從Xml文檔中讀取beanDefinition,對于注冊以及獲取Bean都是使用從父類DefaultListableBeanFactory繼承的方法去實現(xiàn),而唯獨與父類不同的個性化實現(xiàn)就是增加了XmlBeanDefinitionReader類型的reader屬性垒棋,在XmlBeanFactory中主要使用reader屬性對資源文件進(jìn)行讀取和注冊娱挨。

obtainFreshBeanFactory中調(diào)了兩個方法 regreshBeanFactory跟getBeanFactory兩個方法,兩個方法都為抽象方法捕犬,實現(xiàn)類為子類AbstractRefreshableApplicationContext
一步步來跷坝,先來看看createBeanFactory這里面是在干什么
就不說這里是直接new了一個DefaultListableBeanFactory這種一看就能明白的話了 ==!

先看看這個getInternalParentBeanFactory()這個方法是在搞什么飛機(jī)

這個getParent是AbstractApplicationContext中的方法,還記得我們在第一步super(parent)碉碉;這個方法的時候嗎柴钻,在這一步的最后的時候我們就走到了這個Abstract Application的構(gòu)造方法中,而這個構(gòu)造方法的處理是兩步垢粮,第一步是this(),第二步是setParent(parent),這個方法就是把ClassPathXmlApplicationContext中的參數(shù)給傳給了AbstractApplicationContext贴届,但是有個問題就是這個parent是null,而且通過類的繼承體系我們可以發(fā)現(xiàn)這里最終是直接getParent的蜡吧,因為getParent就是獲取一個ApplicationContext毫蚓,而ConfigurableApplicationContext是繼承ApplicationContext,所以這里是返回getParent的

接著我們再來看看new DefaultListableBeanFactory的時候干了啥

又特么是super ==昔善!
也是兩方法

小結(jié):

new DefaultListableBeanFactory:

? ? 1.new?AbstractBeanFactory()

? ? 2.AbstractBeanFactory中設(shè)置parentBeanFactory為ApplicationContext

? ? 3.AbstractAutowireCapableBeanFactory的忽略依賴關(guān)系接口集合屬性中添加了?BeanNameAware,BeanFactoryAware元潘,BeanClassLoaderAware

繼續(xù)往下看

這里的話其實是設(shè)置BeanFactory的一些事件屬性(允許BeanDefinition重寫,允許循環(huán)引用)

然后到了最后也是obtainFreshBeanFactory方法最重要的一步了----->loadBeanDefinitions方法君仆,該方法的實現(xiàn)在子類AbstractXmlApplicationContext中翩概,因為這是個抽象方法

我們先進(jìn)這個new XmlBeanDefinitionReader這個里頭看看

接著往下點
第一個if走的一定是false牲距,因為我們傳過來的是一個DefaultListableBeanFactory,DefaultListableBeanFactory并不是ResourceLoader的實例钥庇,第二個if也是走else 因為DefaultListableBeanFactory也不是EnvironmentCapble的實例? ==!

這里先說一下EnvironmentCapable這個類(注釋翻譯):

接口牍鞠,指示包含并公開Environment引用的組件。

所有Spring應(yīng)用程序上下文都支持EnvironmentCapable评姨, 并且該接口主要用于在框架方法中執(zhí)行instanceof檢查

?這些框架方法接受可能是或可能不是應(yīng)用程序上下文實例的BeanFactory實例难述,以便與環(huán)境交互(如果確實可用)。

ApplicationContext擴(kuò)展了環(huán)境功能吐句,因此公開了 getenvironment()方法龄广;ConfigurableApplicationContext重新定義了getEnvironment方法 并縮小簽名以返回一個ConfigurableEnvironment,其效果是環(huán)境對象是“只讀”的蕴侧,直到從ConfigurableApplicationContext訪問它為止择同,此時也可以對其進(jìn)行配置。

到這里new XmlBeanDefinitionReader(beanFactory); 這個過程就結(jié)束了

總結(jié):

1.AbstractBeanDefinitionReader中設(shè)置registry屬性為DefaultListableBeanFactory

2.AbstractBeanDefinitionReader中設(shè)置resourceLoader 為PathMatchingResourcePatternResolver

3.AbstractBeanDefinitionReader中設(shè)置environment 為StandardEnvironment

4.設(shè)置XmlBeanDefinitionReader的Environment屬性為AbstractApplicationContext中的StandardEnvironment (在我們之前super(parent)這一步的時候給new的)

5.設(shè)置XmlBeanDefinitionReader的ResourceLoader為AbstractXmlApplicationContext

6.設(shè)置XmlBeanDefinitionReader的EntityResolver為ResourceEntityResolver

? ? ResourceEntityResolver:?EntityResolver實現(xiàn)净宵,嘗試通過ResourceLoader(通常是相對于ApplicationContext的資源庫)解析實體引用(如果適用),擴(kuò)展DelegatingEntityResolver以同時提供DTD和XSD查找敲才。允許使用標(biāo)準(zhǔn)XML實體將XML片段包含到應(yīng)用程序上下文定義中,例如將大型XML文件拆分為多個模塊择葡。include路徑可以像往常一樣相對于應(yīng)用程序上下文的資源庫紧武,而不是相對于JVM工作目錄(XML解析器的默認(rèn)值)。

注意:除了相對路徑之外敏储,在當(dāng)前系統(tǒng)根目錄(即jvm工作目錄)中指定文件的每個URL也將相對于應(yīng)用程序上下文進(jìn)行解釋阻星。


第二步是把AbstractXmlApplicationContext作為自己的resourceLoader屬性

EntityResolver:對于解析一個xml,sax 首先會讀取該xml文檔上的聲明,根據(jù)聲明去尋找相應(yīng)的dtd定義,以便對文檔的進(jìn)行驗證,默認(rèn)的尋找規(guī)則,(即:通過網(wǎng)絡(luò),實現(xiàn)上就是聲明DTD的地址URI地址來下載DTD聲明),并進(jìn)行認(rèn)證,下載的過程是一個漫長的過程,而且當(dāng)網(wǎng)絡(luò)不可用時,這里會報錯,就是應(yīng)為相應(yīng)的dtd沒找到,詳細(xì)的用法跟說明大家可以看看這個博客:https://www.cnblogs.com/ghgyj/p/4027796.html已添,我就不再去演示了


initBeanDefinitionReader(beanDefinitionReader):

loadBeanDefinitions(beanDefinitionReader):

使用給定的XmlBeanDefinitionReader加載bean定義妥箕。bean工廠的生命周期由refreshBeanFactory方法處理;因此更舞,此方法只需加載/注冊bean定義椅贱。

這里我們主要是看reader.loadBeanDefinitions(configLocations)這里尚胞,首先這個getConfigLocations(),我們在第二步setConfigLocations()的時候解总,其實就是把我們在new ClassPathXmlApplicationContext時所傳的資源付給了AbstractRefreshableConfigApplicationContext的this.configLocations,所以這里只是把我們之前給設(shè)置進(jìn)來的資源給拿出來而已 ==?

回憶貼上 ==筷黔!

流程開始:

我先把圖全給貼上,然后咱們慢慢分析這個流程

1.?getResourceLoader(): 這一步在new XmlBeanDefinitionReader的時候刊头,就已經(jīng)把resourceLoader給賦值為PathMatchingResourcePatternResolver這個對象黍瞧,在上面就總結(jié)就可以看得到

2.?因為PathMatchingResourcePatternResolver它實現(xiàn)了ResourcePatternResolver接口,所以這里我們直接看getResources(location)這個方法

getPathMatcher()可以獲取一個AntPathMatcher對象

PathMatcher : 實現(xiàn)Ant-style的路徑模式

映射使用以下規(guī)則匹配URL:

原杂?:匹配一個字符

* :匹配零個或多個字符

** : 匹配路徑中的零個或多個目錄

spring:[a-z]+}: 將regexp {[a-z]+} 作為名為“spring”的路徑變量匹配

這里就是在判斷我們路經(jīng)中是否有多個資源具有相同的名稱? 例如:classpath*:applicationContext.xml;classpath*:applicationContext-beans.xml

這里我們就先看這個findAllClassPathResources這個方法:

先來看看這個getClassLoader()方法印颤,這個getResourceLoader也是獲取PathMatchingResourcePatternResolver類中的ResourceLoader,在第一步super(parent); 這里我們已經(jīng)為PathMatchingResourcePatternResolver設(shè)置好了resourceLoader

這個地方我們打個斷點往下看污尉,這樣可以看得更清晰一點:

這個地方就是為了獲取我們資源文件的磁盤路經(jīng)

那么在findAllClassPathResources這個方法的目的就是返回我們資源文件的磁盤路經(jīng)? ?ψ(*`ー′)ψ

現(xiàn)在回到我們AbstractBeanDefinitionReader中

最有意思的來了.... ┗( ▔, ▔ )┛

我們接著下一個loadBeanDefinitions(resources)這個方法往下看

還是loadBeanDefinitions方法膀哲。Resource中包含我們資源文件的磁盤路徑,重頭戲要來了
從xml配置文件中加載BeanDefinition


當(dāng)前正在加載的XML bean定義資源

1.獲取當(dāng)前線程中的EncodedResource集合

2.如果添加失敗被碗,則認(rèn)為是該encodedResource在循環(huán)加載

3.獲取資源文件InputStream對象

doLoadBeanDefinitions才是真正實現(xiàn)的方法

doLoadDocument : 使用配置的DocumentLoader實際加載指定的文檔某宪。

getValidationModeForResource:指示應(yīng)自動檢測驗證模式,isNamespaceAware():XML解析器是否應(yīng)該感知XML名稱空間锐朴,getEntity:返回要使用的EntityResolver兴喂,如果沒有指定,則構(gòu)建默認(rèn)的解析器焚志;this.errorHandler :簡單的ErrorHandler實現(xiàn)衣迷,使用給定的通用日志記錄器實例記錄警告,并重新拋出錯誤以停止XML轉(zhuǎn)換酱酬。

這個地方我覺得有必要提一下getEntityResolver這個方法:

這個地方在看obtainFreshBeanFactory 方法的時候嗎壶谒,有給這個entityResolver屬性賦值過,所以 this.entityResolver =?ResourceEntityResolver 對象膳沽,因此也是直接返回這個對象


Spring的默認(rèn)DocumentLoader實現(xiàn)汗菜。只需使用標(biāo)準(zhǔn)的jaxp配置的XML解析器加載 Document 。如果您想更改用于加載文檔的 DocumentBuilder挑社,那么一種策略是在啟動JVM時定義相應(yīng)的Java系統(tǒng)屬性陨界。例如,要使用Oracle DocumentBuilder痛阻,可以這樣啟動應(yīng)用程序:java -Djavax.xml.parsers.DocumentBuilderFactory=oracle.xml.jaxp.JXDocumentBuilderFactory MyMainClass(存翻譯注釋)


需要注意的是我們在調(diào)用過來是validationMode所傳的值為默認(rèn)值:VALIDATION_AUTO(指示應(yīng)自動檢測驗證模式)


創(chuàng)建一個DocumentBuilder菌瘪,此bean定義閱讀器將使用它解析XML文檔≮宓保可以在子類中重寫俏扩,添加構(gòu)建器的進(jìn)一步初始化

接著來看下一個方法

int count = registerBeanDefinitions(doc, resource);

BeanDefinitionDocumentReader:用于解析包含Spring bean定義的XML文檔,由XmlBeanDefinitionReader用于實際解析DOM文檔。


DefaultBeanDefinitionDocumentReader :? BeanDefinitionDocumentReader接口的默認(rèn)實現(xiàn)弊添, 該接口根據(jù)“Spring -beans”DTD和XSD格式(Spring的默認(rèn)XML bean定義格式)讀取bean定義动猬。所需XML文檔的結(jié)構(gòu)、元素和屬性名都硬編碼在這個類中表箭。(當(dāng)然赁咙,如果需要生成這種格式,可以運行轉(zhuǎn)換)免钻。不需要是XML文檔的根元素:這個類將解析XML文件中的所有bean定義元素彼水,而不管實際的根元素是什么。


int countBefore = getRegistry().getBeanDefinitionCount() : 獲取bean定義對象Map的大小

getRegistry其實就是獲取之前我們給XmlBeanDefinitionReader設(shè)置的DefaultListableBeanFactory,是在AbstractXmlApplicationContext類的loadBeanDefinitions方法中new XmlBeanDefinitionReader的時候給賦值了

documentReader.registerBeanDefinitions(doc, createReaderContext(resource)):

先來看看這個createReaderContext方法是做了什么吧:

先看看這個createDefaultNamespaceHandlerResolver是做什么极舔,返回什么吧
getResourceLoader()看方法名就知道是在干什么凤覆,這里是直接走true,因為我們在之前已經(jīng)給這個屬性賦值過了拆魏,值為PathMatchingResourcePatternResolver 對象盯桦,剛剛就把圖給貼出來

DefaultNamespaceHandlerResolver:NamespaceHandlerResolver接口的默認(rèn)實現(xiàn)慈俯。根據(jù)映射文件中包含的映射將名稱空間uri解析為實現(xiàn)類。

String handlerMappingsLocation? =?"META-INF/spring.handlers"拥峦;? (沒啥意思的方法我就不貼圖了吧 ==),handlerMappingsLocation =PathMatchingResourcePatternResolver.getClassLoader();?


這里就需要記一下了贴膘,因為后面會有地方用到 (感覺像是在說廢話)

? ? ? ? ? ? ? ? ReaderContext (設(shè)置屬性):

? ? ? ? ? ? ? ? ? ? ? ? 1.resource =?Resource

? ? ? ? ? ? ? ? ? ? ? ? 2.problemReporter =?FailFastProblemReporter

? ? ? ? ? ? ? ? ? ? ? ? 3.?eventListener? ?=??EmptyReaderEventListener

? ? ? ? ? ? ? ? ? ? ? ? 4.?sourceExtractor? =?NullSourceExtractor

? ??????????????XmlReaderContext (設(shè)置屬性):

? ? ? ? ? ? ? ? ? ? ? ? 1. reader? =??XmlBeanDefinitionReader

? ? ? ? ? ? ? ? ? ? ? ? 2.?namespaceHandlerResolver =??DefaultNamespaceHandlerResolver

? ? ? ? ? ? ? ? createReaderContext() : 創(chuàng)建XmlReaderContext對象,接下來我們回到registerBeanDefinitions方法:

this.delegate = createDelegate(getReaderContext(), root, parent):

初始化默認(rèn)的懶加載初始化、自動裝配略号、依賴項檢查設(shè)置刑峡、init-method、destroy-method和merge設(shè)置玄柠,通過返回到給定的父元素來支持嵌套的“bean”元素用例突梦,以防默認(rèn)值沒有在本地顯式設(shè)置。

1.創(chuàng)建一個與所提供的相關(guān)聯(lián)的新的BeanDefinitionParserDelegate

初始化默認(rèn)的懶加載初始化羽利、自動裝配宫患、依賴項檢查設(shè)置、init-method这弧、destroy-method和merge設(shè)置撮奏。通過返回到給定的父元素來支持嵌套的“bean”元素用例,以防默認(rèn)值沒有在本地顯式設(shè)置当宴。

this.defaults =?DocumentDefaultsDefinition對象 (在標(biāo)準(zhǔn)的Spring XML bean定義文檔中畜吊,簡單的JavaBean包含在<beans>級別指定的默認(rèn)值:default-lazy-init,default-autowire户矢,etc)

populateDefaults :?

首先是通過我們Xml文件的根節(jié)點(<beans>)玲献,然后再來判斷它的一些屬性有沒有配置值,如果沒有則賦予默認(rèn)值
這里還有最后這一步梯浪,直接點進(jìn)去看一下吧
sourceExtractor = NullSourceExtractor捌年,這里我們在之前創(chuàng)建XmlReaderContext對象時,已經(jīng)為父類ReaderContext給賦值過了
這個方法只返回null??( ̄ェ ̄;)

到這里populateDefaults這個方法就算是結(jié)束了挂洛,來個很有儀式感的總結(jié)吧:?設(shè)置Xml資源文件根節(jié)點某些屬性默認(rèn)的值

DocumentDefaultsDefinition :這個使我們傳入populateDefaults方法中的this.defaults礼预,該DocumentDefaultsDefinition 是不可變的,所以接下來的設(shè)置是有必要給記錄下來的

1.DocumentDefaultsDefinition 設(shè)置lazyInit為false

2.DocumentDefaultsDefinition 設(shè)置merge為false

3.DocumentDefaultsDefinition 設(shè)置autowire為no

4.DocumentDefaultsDefinition? 設(shè)置source 為null


this.readerContext.fireDefaultsRegistered(this.defaults):

調(diào)用XmlReaderContext的方法虏劲,在父類ReaderContext中找到實現(xiàn)


this.evenListener =?EmptyReaderEventListener

先打開這個類來看看是咋樣的吧

.........

? ? 這個地方我可以確定我沒有搞錯托酸,因為這個地方反正一定是從創(chuàng)建XmlReaderContext的時候給賦值的

我要證明自己沒有搞錯.....
看來事實就是這樣

那這里就啥都沒干....然后返回delegate....那 initDefaults方法就走完了......┓(;′_`)┏.....


開始下一步吧


namespaceUri 等于空 或者 http://www.springframework.org/schema/beans

接下來是獲取beans的profile屬性,并且從環(huán)境中去對比是否是spring.profiles.active對于的資源文件柒巫,這個大家應(yīng)該都有用過這種方式励堡,我簡單說下吧


首先在AbstractEnvironment中找到該實現(xiàn),這個地方我們主要是看這個isProfileActive這個方法

1.校驗文件不為空并且不以 ! 開頭

2.獲取一個設(shè)置好的profile資源集合堡掏,下圖中有實現(xiàn)方式

3.校驗是否存在activeProfiles集合中应结,或者設(shè)置好的profile資源集合為空,校驗currentActiveProfiles為空 并且 defaultProfiles集合中存在該profile(doGetDefaultProfiles方法的原理與doGetActiveProfiles一樣)


如果存在于當(dāng)前環(huán)境中,就把他添加進(jìn)activeProfiles集合中

下面我們就寫個Demo來玩玩這個profile :


在beans加個profile的屬性鹅龄,值就為dev吧揩慕,隨便搞個值就行了


還記得我們在校驗profile的時候就會判斷是否為空,不為空的話就會在環(huán)境中找spring.profiles.active和spring.profiles.default 如果找到并且等于這個profile屬性的值的話扮休,如果沒找到的話就直接return了

既然是在環(huán)境中找迎卤,那么我們往環(huán)境中加上這個映射不就可以了嗎

沒毛病

好了,再回到我們的doRegisterBeanDefinitions方法中接著往下看:

preProcessXml(root):


在開始處理bean定義之前肛炮,允許通過處理任何自定義元素類型來擴(kuò)展XML止吐。對于任何其他定制的XML預(yù)處理宝踪,此方法都是一個自然的擴(kuò)展點侨糟。默認(rèn)實現(xiàn)為空。例如瘩燥,子類可以覆蓋此方法秕重,將定制元素轉(zhuǎn)換為標(biāo)準(zhǔn)的Spring bean定義。實現(xiàn)者可以通過相應(yīng)的訪問器訪問解析器的bean定義閱讀器和底層XML資源厉膀。

parseBeanDefinitions(root, this.delegate):


這里我們主要來看這個parseDefaultElement方法溶耘,反正它本來就是進(jìn)這里,哈哈哈


哈哈哈服鹅,一個一個來凳兵,這里可不能漏

1.import


解析import

一開始是判斷resource屬性是否為空,大家應(yīng)該也都用過import企软,我們通常會使用import來分割規(guī)劃我們的applicationContext資源文件庐扫,而resource就是指向這些分割的文件的,那當(dāng)然是不能為空的(雖然很廢話仗哨,但還是得說)

location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location): 從AbstractBeanDefinitionReader中獲取environment形庭,我們在看obtainFreshBeanFactory該步驟的源碼中,AbstractBeanDefinitionReader的environment給設(shè)置為了StandardEnvironment對象厌漂,還有就是resolveRequiredPlaceholders這個方法我們在看setConfigLocations的源碼時也說過萨醒,這個地方就是在解析我們傳入的占位符(${JAVA_HOME}),大家還記得我寫的那個demo嗎苇倡。new ClassPathXmlApplicationContext("classpath*:applicationContext.xml;${JAVA_HOME}");

absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute() : 校驗該資源路徑是否是相對路徑或者絕對路徑

1.返回給定的資源位置是否是URL:一個特殊的“類路徑”或“類路徑*”偽URL或一個標(biāo)準(zhǔn)URL富纸。(不等于null并且以classpath*:開頭)

2.直接使用URL對象的isAbsolute方法來判斷是否是一個絕對路徑


然后又按照之前的流程走一遍,哈哈哈哈旨椒,就不用說了吧

Resource[] actResArray = actualResources.toArray(new Resource[0])

getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele))

現(xiàn)在來看最后這個地方是在處理啥:

this.eventListener = EmptyReaderEventListener(在new XmlReaderContext時賦的值 )


之前也看過這個類胜嗓,里頭不做任何處理

2.alias

首先也不用說了,那就是判斷alias標(biāo)簽的兩個必須要填的屬性是否為空钩乍,這個大家肯定是經(jīng)常都會使用到的辞州,就算現(xiàn)在沒有使用過,以前肯定也是使用過的寥粹,直接進(jìn)方法看實現(xiàn)吧

getReaderContext().getRegistry().registerAlias(name, alias):

這種圖真美~

首先我們知道這里獲取的reader是我們的XmlBeanDefinitionReader對象变过,然后我們調(diào)用了它的getRegistry方法埃元,然后就走到了AbstractBeanDefinitionReader中,然后獲取它的registry屬性媚狰,這里我還是要多嘴一句岛杀,還是我們在看obtainFreshBeanFactory()方法的源碼時給設(shè)置的屬性,屬性值為DefaultListableBeanFactory,實現(xiàn)方法是在SimpleAliasRegistry類中


checkForAliasCircle(name, alias):檢查給定名稱是否已經(jīng)指向另一個方向的給定別名作為別名

this.aliasMap.put(alias, name);? 添加進(jìn)alias與 id 的映射

getValue是獲取到Id崭孤,因為我們一個bean可以有多個別名类嗤,所以得以別名為key,id為value

字?jǐn)?shù)太多辨宠,沒辦法發(fā)布 ==遗锣,后面的寫在第二篇,感謝瀏覽喲~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末嗤形,一起剝皮案震驚了整個濱河市精偿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌赋兵,老刑警劉巖笔咽,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異霹期,居然都是意外死亡叶组,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進(jìn)店門历造,熙熙樓的掌柜王于貴愁眉苦臉地迎上來甩十,“玉大人,你說我怎么就攤上這事帕膜≡嫜酰” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵垮刹,是天一觀的道長达吞。 經(jīng)常有香客問我,道長荒典,這世上最難降的妖魔是什么酪劫? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮寺董,結(jié)果婚禮上覆糟,老公的妹妹穿的比我還像新娘。我一直安慰自己遮咖,他們只是感情好滩字,可當(dāng)我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般麦箍。 火紅的嫁衣襯著肌膚如雪漓藕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天挟裂,我揣著相機(jī)與錄音享钞,去河邊找鬼。 笑死诀蓉,一個胖子當(dāng)著我的面吹牛栗竖,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播渠啤,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼狐肢,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了埃篓?” 一聲冷哼從身側(cè)響起处坪,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤根资,失蹤者是張志新(化名)和其女友劉穎架专,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體玄帕,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡部脚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了裤纹。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片委刘。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖鹰椒,靈堂內(nèi)的尸體忽然破棺而出锡移,到底是詐尸還是另有隱情,我是刑警寧澤漆际,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布淆珊,位于F島的核電站,受9級特大地震影響奸汇,放射性物質(zhì)發(fā)生泄漏施符。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一擂找、第九天 我趴在偏房一處隱蔽的房頂上張望戳吝。 院中可真熱鬧,春花似錦贯涎、人聲如沸听哭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽陆盘。三九已至且警,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間礁遣,已是汗流浹背斑芜。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留祟霍,地道東北人杏头。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像沸呐,于是被迫代替她去往敵國和親醇王。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容