Spring配置類后置處理器ConfigurationClassPostProcessor解析配置類的過程

本文將繼續(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)行遍歷解析。

  1. 如果bean實(shí)現(xiàn)自 AnnotatedBeanDefinition 接口,則進(jìn)行注解bean定義的解析流程,詳見下面章節(jié)中的各步驟說明。
  2. 如果bean實(shí)現(xiàn)自 AbstractBeanDefinition 接口,且指定了beanClass 屬性,則進(jìn)行抽象bean定義的解析;
  3. 否則作為常規(guī)配置類進(jìn)行解析
  4. 逐個(gè)遍歷解析完畢配置類之后,判斷是否需要延遲導(dǎo)入,如果存在需要延遲導(dǎo)入的處理器,直接進(jìn)行處理。

1. 注解bean定義 AnnotatedBeanDefinition 的解析流程

實(shí)際調(diào)用到 ConfigurationClassParser#processConfigurationClass 方法進(jìn)行配置類解析,這里的參數(shù)是將元數(shù)據(jù)和bean名稱進(jìn)行封裝成為一個(gè)整體

  1. 首先判斷是否指定了 @Conditional 注解及子注解,需要在解析環(huán)境進(jìn)行跳過
  2. 判斷給定的bean類是否在給定的類集合中
    1. 如果已經(jīng)存在,則判斷bean類是否是通過 @Import 導(dǎo)入的,且已存在的bean類也是通過 @Import 導(dǎo)入的,則直接將當(dāng)前判斷的bean類合并到已經(jīng)存在的bean類中,直接返回。
    2. 如果已經(jīng)存在,且當(dāng)前判斷的bean類不是通過 @Import 導(dǎo)入的,則從給定的類集合中刪除。
  3. 根據(jù)bean定義中的類,解析得到源類 SourceClass
  4. 調(diào)用方法 ConfigurationClassParser#doProcessConfigurationClass 執(zhí)行處理配置類的動(dòng)作,并最終返回父類,遞歸進(jìn)行處理,直到最頂級的父類
    1. 判斷配置類是否添加了 @Component 注解,如果添加了,則還需要解析成員類,也就是嵌套類,這里我們先不討論
    2. 如果配置類添加了 @PropertySources 注解,并且配置類解析器的環(huán)境變量實(shí)現(xiàn)了 ConfigurableEnvironment 接口,則調(diào)用 ConfigurationClassParser#processPropertySource 方法處理 @PropertySources 注解,詳細(xì)解析過程見下面章節(jié)

1.4.2 ConfigurationClassParser#processPropertySource 方法處理 @PropertySources 注解的過程

  1. 獲取 @PropertySources 注解中的 name 屬性值,如果未指定,則參數(shù)值為 null
  2. 獲取 @PropertySources 注解中的 encoding 屬性值,如果未指定,則參數(shù)值為 null
  3. 獲取 @PropertySources 注解中的 value 屬性值,解析為位置數(shù)組,如果未指定,則直接判定為失敗,必須要指定 value 屬性
  4. 獲取 @PropertySources 注解中的 ignoreResourceNotFound 屬性值,默認(rèn)為false,判斷是否忽略找不到資源的錯(cuò)誤。
  5. 獲取 @PropertySources 注解中的 factory 屬性值,作為屬性源工廠 PropertySourceFactory 的類名,默認(rèn)為 PropertySourceFactory.class。根據(jù)屬性值創(chuàng)建屬性源工廠實(shí)例對象,值為 PropertySourceFactory.class 時(shí),工廠為 DefaultPropertySourceFactory 實(shí)例。
  6. 遍歷上面第3步得到的位置數(shù)組,將位置解析成為絕對地址,并加載可用資源,進(jìn)行解碼后,使用工廠實(shí)例創(chuàng)建屬性源對象,添加到屬性源中
  7. 獲取 @ComponentScan 注解的屬性值,如果指定了該注解,且在注冊bean REGISTER_BEAN 時(shí)不需要跳過,則遍歷屬性值
    1. 使用組件掃描解析器掃描給定的包名路徑,如何完成掃描請查看下面的章節(jié)
    2. 遍歷掃描出來的bean定義集合,如果bean定義存在源定義則使用源定義進(jìn)行進(jìn)一步判斷,判斷bean定義是否是配置類,如果是配置類,則繼續(xù)調(diào)用解析配置類方法進(jìn)行遞歸解析
  8. 調(diào)用方法 ConfigurationClassParser#processImports 處理 @Import 注解(詳細(xì)的處理過程,將另開文章進(jìn)行說明
  9. 獲取 @ImportResource 注解,讀取 location ,替換 location 中的參數(shù)值,讀取 reader 屬性值,解析為bean定義讀取器類,保存對應(yīng)的映射關(guān)系到配置類的導(dǎo)入資源集合中
  10. 解析配置類,得到配置類中使用 @Bean 注解的方法集合,將獲取到的方法元數(shù)據(jù)創(chuàng)建為 BeanMethod ,保存到配置類的 BeanMethod 屬性中(具體的注解方法掃描過程,我們另開文章說明
  11. 遞歸遍歷處理接口上的默認(rèn) @Bean 方法
  12. 遞歸處理超類,如果超類不為空,且不是 java 開頭,則添加到已知超類集合中,返回超類,繼續(xù)遞歸遍歷處理
  13. 返回null

1.4.2.7.1 ComponentScanAnnotationParser#parse 解析配置對象,并掃描指定包下bean對象的流程

  1. 創(chuàng)建掃描器 ClassPathBeanDefinitionScanner,這也可以證明在聲明上下文時(shí)聲明的掃描器是給程序員用的,Spring自己使用的時(shí)候會重新創(chuàng)建一個(gè)新的掃描器,這個(gè)掃描器在創(chuàng)建的時(shí)候會判斷是否使用默認(rèn)的過濾器。
  2. 獲取指定的Bean名稱生成器,如果給定的生成器類型為 BeanNameGenerator ,認(rèn)為使用的是內(nèi)部默認(rèn)的名稱生成器,否則使用給定的名稱生成器類,創(chuàng)建對應(yīng)生成器實(shí)例,并綁定到掃描器上。
  3. 判斷指定的范圍代理選項(xiàng) scopedProxy ,指定是使用JDK代理還是CGLIB代理,或是不使用代理,并保存使用代理的選項(xiàng)。如果不使用范圍代理,需要獲取范圍解析選項(xiàng) scopeResolver ,并實(shí)例化解析器對象綁定到掃描器上。
  4. 將設(shè)置的資源模式 resourcePattern 綁定到掃描器上
  5. 將設(shè)置的包含類型 includeFilters 和排除類型 excludeFilters 綁定到掃描器上
  6. 將設(shè)置的延遲加載選項(xiàng) lazyInit 設(shè)置在掃描器中的bean定義的默認(rèn)值上
  7. 獲取設(shè)置的掃描包名屬性 basePackages ,替換其中的環(huán)境變量值,并將值轉(zhuǎn)換為數(shù)組,多個(gè)包名之間可以使用 ,; \t\n 進(jìn)行分隔,將獲得的包名集合保存到包地址集合中
  8. 獲取指定的包類名屬性值 basePackageClasses ,獲取其包名保存在包地址集合中
  9. 如果最終包地址集合仍然沒有值,則默認(rèn)添加當(dāng)前配置類所在的包名
  10. 添加排除過濾器,用于排除掃描結(jié)果中類名跟配置類類名一直的結(jié)果,避免配置類重復(fù)加載
  11. 調(diào)用 ClassPathBeanDefinitionScanner#doScan 執(zhí)行實(shí)際包掃描的動(dòng)作。(實(shí)際掃描包的流程,見下文說明
  12. 返回掃描出來的bean定義集合。

1.4.2.7.1.11 ClassPathBeanDefinitionScanner#doScan 執(zhí)行實(shí)際包掃描的流程

  1. 聲明bean定義集合
  2. 遍歷包名數(shù)組,掃描包下符合條件的候選Bean定義集合,遍歷候選Bean定義集合(具體的掃描過程我們另開文章再來說明
    1. 使用范圍源數(shù)據(jù)解析器判斷是否添加了 @Scope 注解,如果設(shè)置了該注解,以該bean定義中設(shè)置的范圍和代理類型為準(zhǔn),否則使用默認(rèn)的單例范圍。并將解析出來的范圍信息保存在bean定義中
    2. 使用bean名稱生成器生成Bean名稱,常規(guī)做法是直接根據(jù)類名,將首字母小寫后作為bean名稱,內(nèi)部類名稱中會產(chǎn)生 .。
    3. 如果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)裝配候選。
    4. 如果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)容
    5. 檢查掃描出來的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定義。
  3. 返回處理完畢的bean定義集合
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容