本文將繼續(xù)跟著前文中的配置類判斷進(jìn)行,方法入口位于 org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>) ,在這里,已注冊bean定義中的配置類bean定義已經(jīng)被篩選到候選對象集合中,逐個(gè)進(jìn)行遍歷解析。
- 如果bean實(shí)現(xiàn)自
AnnotatedBeanDefinition接口,則進(jìn)行注解bean定義的解析流程,詳見下面章節(jié)中的各步驟說明。 - 如果bean實(shí)現(xiàn)自
AbstractBeanDefinition接口,且指定了beanClass 屬性,則進(jìn)行抽象bean定義的解析; - 否則作為常規(guī)配置類進(jìn)行解析
- 逐個(gè)遍歷解析完畢配置類之后,判斷是否需要延遲導(dǎo)入,如果存在需要延遲導(dǎo)入的處理器,直接進(jìn)行處理。
1. 注解bean定義 AnnotatedBeanDefinition 的解析流程
實(shí)際調(diào)用到 ConfigurationClassParser#processConfigurationClass 方法進(jìn)行配置類解析,這里的參數(shù)是將元數(shù)據(jù)和bean名稱進(jìn)行封裝成為一個(gè)整體
- 首先判斷是否指定了
@Conditional注解及子注解,需要在解析環(huán)境進(jìn)行跳過 - 判斷給定的bean類是否在給定的類集合中
- 如果已經(jīng)存在,則判斷bean類是否是通過
@Import導(dǎo)入的,且已存在的bean類也是通過@Import導(dǎo)入的,則直接將當(dāng)前判斷的bean類合并到已經(jīng)存在的bean類中,直接返回。 - 如果已經(jīng)存在,且當(dāng)前判斷的bean類不是通過
@Import導(dǎo)入的,則從給定的類集合中刪除。
- 如果已經(jīng)存在,則判斷bean類是否是通過
- 根據(jù)bean定義中的類,解析得到源類
SourceClass - 調(diào)用方法
ConfigurationClassParser#doProcessConfigurationClass執(zhí)行處理配置類的動(dòng)作,并最終返回父類,遞歸進(jìn)行處理,直到最頂級的父類- 判斷配置類是否添加了
@Component注解,如果添加了,則還需要解析成員類,也就是嵌套類,這里我們先不討論 - 如果配置類添加了
@PropertySources注解,并且配置類解析器的環(huán)境變量實(shí)現(xiàn)了ConfigurableEnvironment接口,則調(diào)用ConfigurationClassParser#processPropertySource方法處理@PropertySources注解,詳細(xì)解析過程見下面章節(jié)
- 判斷配置類是否添加了
1.4.2 ConfigurationClassParser#processPropertySource 方法處理 @PropertySources 注解的過程
- 獲取
@PropertySources注解中的name屬性值,如果未指定,則參數(shù)值為null - 獲取
@PropertySources注解中的encoding屬性值,如果未指定,則參數(shù)值為null - 獲取
@PropertySources注解中的value屬性值,解析為位置數(shù)組,如果未指定,則直接判定為失敗,必須要指定value屬性 - 獲取
@PropertySources注解中的ignoreResourceNotFound屬性值,默認(rèn)為false,判斷是否忽略找不到資源的錯(cuò)誤。 - 獲取
@PropertySources注解中的factory屬性值,作為屬性源工廠PropertySourceFactory的類名,默認(rèn)為PropertySourceFactory.class。根據(jù)屬性值創(chuàng)建屬性源工廠實(shí)例對象,值為PropertySourceFactory.class時(shí),工廠為DefaultPropertySourceFactory實(shí)例。 - 遍歷上面第3步得到的位置數(shù)組,將位置解析成為絕對地址,并加載可用資源,進(jìn)行解碼后,使用工廠實(shí)例創(chuàng)建屬性源對象,添加到屬性源中
- 獲取
@ComponentScan注解的屬性值,如果指定了該注解,且在注冊beanREGISTER_BEAN時(shí)不需要跳過,則遍歷屬性值- 使用組件掃描解析器掃描給定的包名路徑,如何完成掃描請查看下面的章節(jié)
- 遍歷掃描出來的bean定義集合,如果bean定義存在源定義則使用源定義進(jìn)行進(jìn)一步判斷,判斷bean定義是否是配置類,如果是配置類,則繼續(xù)調(diào)用解析配置類方法進(jìn)行遞歸解析
- 調(diào)用方法
ConfigurationClassParser#processImports處理@Import注解(詳細(xì)的處理過程,將另開文章進(jìn)行說明) - 獲取
@ImportResource注解,讀取location,替換location中的參數(shù)值,讀取reader屬性值,解析為bean定義讀取器類,保存對應(yīng)的映射關(guān)系到配置類的導(dǎo)入資源集合中 - 解析配置類,得到配置類中使用
@Bean注解的方法集合,將獲取到的方法元數(shù)據(jù)創(chuàng)建為BeanMethod,保存到配置類的BeanMethod屬性中(具體的注解方法掃描過程,我們另開文章說明) - 遞歸遍歷處理接口上的默認(rèn)
@Bean方法 - 遞歸處理超類,如果超類不為空,且不是
java開頭,則添加到已知超類集合中,返回超類,繼續(xù)遞歸遍歷處理 - 返回null
1.4.2.7.1 ComponentScanAnnotationParser#parse 解析配置對象,并掃描指定包下bean對象的流程
- 創(chuàng)建掃描器
ClassPathBeanDefinitionScanner,這也可以證明在聲明上下文時(shí)聲明的掃描器是給程序員用的,Spring自己使用的時(shí)候會重新創(chuàng)建一個(gè)新的掃描器,這個(gè)掃描器在創(chuàng)建的時(shí)候會判斷是否使用默認(rèn)的過濾器。 - 獲取指定的Bean名稱生成器,如果給定的生成器類型為
BeanNameGenerator,認(rèn)為使用的是內(nèi)部默認(rèn)的名稱生成器,否則使用給定的名稱生成器類,創(chuàng)建對應(yīng)生成器實(shí)例,并綁定到掃描器上。 - 判斷指定的范圍代理選項(xiàng)
scopedProxy,指定是使用JDK代理還是CGLIB代理,或是不使用代理,并保存使用代理的選項(xiàng)。如果不使用范圍代理,需要獲取范圍解析選項(xiàng)scopeResolver,并實(shí)例化解析器對象綁定到掃描器上。 - 將設(shè)置的資源模式
resourcePattern綁定到掃描器上 - 將設(shè)置的包含類型
includeFilters和排除類型excludeFilters綁定到掃描器上 - 將設(shè)置的延遲加載選項(xiàng)
lazyInit設(shè)置在掃描器中的bean定義的默認(rèn)值上 - 獲取設(shè)置的掃描包名屬性
basePackages,替換其中的環(huán)境變量值,并將值轉(zhuǎn)換為數(shù)組,多個(gè)包名之間可以使用,; \t\n進(jìn)行分隔,將獲得的包名集合保存到包地址集合中 - 獲取指定的包類名屬性值
basePackageClasses,獲取其包名保存在包地址集合中 - 如果最終包地址集合仍然沒有值,則默認(rèn)添加當(dāng)前配置類所在的包名
- 添加排除過濾器,用于排除掃描結(jié)果中類名跟配置類類名一直的結(jié)果,避免配置類重復(fù)加載
- 調(diào)用
ClassPathBeanDefinitionScanner#doScan執(zhí)行實(shí)際包掃描的動(dòng)作。(實(shí)際掃描包的流程,見下文說明) - 返回掃描出來的bean定義集合。
1.4.2.7.1.11 ClassPathBeanDefinitionScanner#doScan 執(zhí)行實(shí)際包掃描的流程
- 聲明bean定義集合
- 遍歷包名數(shù)組,掃描包下符合條件的候選Bean定義集合,遍歷候選Bean定義集合(具體的掃描過程我們另開文章再來說明)
- 使用范圍源數(shù)據(jù)解析器判斷是否添加了
@Scope注解,如果設(shè)置了該注解,以該bean定義中設(shè)置的范圍和代理類型為準(zhǔn),否則使用默認(rèn)的單例范圍。并將解析出來的范圍信息保存在bean定義中 - 使用bean名稱生成器生成Bean名稱,常規(guī)做法是直接根據(jù)類名,將首字母小寫后作為bean名稱,內(nèi)部類名稱中會產(chǎn)生
.。 - 如果bean定義實(shí)現(xiàn)了
AbstractBeanDefinition接口,需要進(jìn)一步處理bean定義:- 設(shè)置bean定義默認(rèn)值,是否延遲加載、自動(dòng)裝配模式、依賴檢查、初始化方法名稱、銷毀方法名稱為默認(rèn)值,且說明指定的初始化方法和銷毀方法均不是默認(rèn)的
- 如果指定了自動(dòng)裝配候選規(guī)則,則使用規(guī)則同名稱進(jìn)行匹配,如果匹配成功,則認(rèn)為該bean作為自動(dòng)裝配候選,否則不可以作為自動(dòng)裝配候選。
- 如果bean定義實(shí)現(xiàn)了
AnnotatedBeanDefinition接口,則處理bean定義中的基本注解,設(shè)置到bean定義中。具體有:-
@Lazy注解,指定是否需要延遲初始化 -
@Primary注解,指定當(dāng)多個(gè)bean滿足自動(dòng)裝配的條件時(shí),當(dāng)前bean是否作為優(yōu)先選擇對象 -
@DependsOn注解,指定當(dāng)前bean的依賴對象,需要在依賴bean初始化之后初始化,銷毀之后銷毀 -
@Role注解,指定角色類型 -
@Description注解,指定說明內(nèi)容
-
- 檢查掃描出來的bean定義同現(xiàn)有的已經(jīng)掃描出來的bean定義是否沖突,如果返回false,則直接跳過該掃描出來的bean,否則創(chuàng)建一個(gè)新的bean定義并添加到bean定義集合中。(添加方法為
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry),具體的執(zhí)行邏輯將另開文章說明)- 如果bean定義集合中不存在給定名稱的bean定義,則直接認(rèn)為不沖突
- 得到已經(jīng)存在的bean定義,判斷是否存在源bean定義,如果存在源,則使用源來進(jìn)行比較
- 比較已經(jīng)存在bean定義跟新掃描出來的bean定義是否兼容,比較的內(nèi)容有:原有bean定義沒有實(shí)現(xiàn)
ScannedGenericBeanDefinition接口,也就說原有bean不是掃描得到的;掃描文件中的文件源不為空,且文件源同原有文件的文件源一致,也就是說同一個(gè)源文件被掃描出來兩次;原有bean定義和掃描出來的bean定義對象一致,也就是等價(jià)類被掃描兩次。滿足上面條件的認(rèn)為是兼容的,直接返回false,不繼續(xù)處理掃描出來的這個(gè)對象 - 否則拋出
ConflictingBeanDefinitionException異常,存在沖突的bean定義。
- 使用范圍源數(shù)據(jù)解析器判斷是否添加了
- 返回處理完畢的bean定義集合