天天看點

spring-ioc源碼

spring-ioc源碼

需要解決的問題
  1. beanfactory和factorybean的差別
  2. beanfactorypostprocessor在spring中的作用
  3. springioc的加載過程
  4. bean的生命周期
  5. spring中有哪些擴充接口及調用時機

大綱

1. 主要流程-springioc的加載過程
  1. 執行個體化容器AnnotationConfigApplicationContext
  2. 執行個體化工廠DefaultListAbleBeanFactory
  3. 執行個體化建立beanDefination讀取器 annotatedBeandefinationReader
  4. 建立beandefination掃描器:classPathBeanDefiantionScanner
  5. 注冊配置類為beanDefination: register方法,annotatedClasses
  6. refresh
  7. invokeBeanfacorypostProcessors(beanfactory)
  8. finishBeanfactoryinitialization(beanfactory)
2. 主要流程-spring bean的生命周期

springioc的加載過程

  1. 首先準備個例子如下(插入圖檔)
  2. 2.springioc的容器加載流程
  3. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);      

    annotationConfigApplicationContext的關系圖如下

    建立annotationConfigApplicationContext對象

    首先看這個構造方法

  4. ​ 3.4 如果沒有配置類會位元組傳回

    ​ 3.5 處理排序

    ​ 3.6 解析配置類,可能是full類,也可能是lite類

    ​ 3.7 在第六步的時候隻是注冊了部分Bean,但是如@Import@Bean,是沒有被注冊的。

    因為可以有多個配置類,是以需要循環處理。我們的配置類的BeanDefinition是AnnotatedBeanDefinition的執行個體,是以會進入第一個

    if

    重點在于doProcessConfigurationClass方法,需要特别注意,最後一段代碼,會把configClass放于一個map,

  1. 這是一個有參構造方法,可以接受多個配置類,不過一般情況隻傳入一個配置類。
  2. 這個配置類有倆種情況,一種是傳統意義上的帶@Configutarion注解的配置類,還有一種是帶@Component,@import,@importResource等注解的配置類,在spring中前者被稱為full配置類,後者叫lite配置類,也叫普通bean

    檢視這個this()方法都幹了什麼,如圖

    spring-ioc源碼
    spring-ioc源碼
    首先無參構造方法中就是對讀取器read和掃描器進行了執行個體化,reader的類型是annotatedbeandefinitionReader,可以看出他是一個 bean定義讀取器,scanner的類型是classpathbeandefinitionscanner,它僅僅是外面手動調用.scan方法,
  3. 執行個體化工廠:defaultListableBeanFactory
    spring-ioc源碼
    DefaultListableBeanFactory的關系圖
    spring-ioc源碼
    defaultlistablebeanfactory是bean的工廠,是用來生産和獲得bean。
  4. 執行個體化建beanDefinition讀取器:annotatedbeandefinitionReader

    主要負責倆件事

    1. 注冊内置beanPostprocrssor
      1. 注冊相關的beandefinition
    spring-ioc源碼

    ​ 回到annotationconfigapplicationcontext的無參構造方法,看annotatedbeandefinitionreader

    spring-ioc源碼
    這裡beandefinitionregistry是annotationconfigapplicationcontext的執行個體,這裡調用其他類構造方法
    spring-ioc源碼
    spring-ioc源碼
    spring-ioc源碼
    這個方法就是注冊spring的内置的多個bean
    1. 判斷容器内是否存在configurationclasspostprocessor bean
      1. 如果不存在,就通過rootBeanDefinition的構造方法獲得configurationclasspostprocessor的beandefinition,rootbeandefinition是beandefinition的子類
      2. 執行registerpostprocessor方法,注冊bean
    5 beandefinition是什麼?
    spring-ioc源碼
    1. beanmetadataelement接口,beandefinition中繼資料,傳回該bean的來源
    2. attributeaccessor接口,提供對beandefinition屬性操作能力

      spring-ioc源碼
    3. 他是用來描述bean,裡面存放着關于bean的一些列資訊,比如bean的作用域,bean所對應的class,是否懶加載,是否primary等等。
    4. registerpostprocessor
      spring-ioc源碼
      這方法為beandefinition設定了role,ROLE_INFRASTRUCTURE代表這是spring内部的,并非使用者定義的。然後又調用了registerBeandefinition方法,他的實作類是defaultlistablebeanfactory:
      spring-ioc源碼
      spring-ioc源碼
      從這裡可以看出defaultlistablebeanfactory就是我們所說的容器,裡面放着beandefinitionmap,beandifinitionnames,前者呢beanname做key,beandefinition做value,後者是個集合,裡面放beanname。
      spring-ioc源碼
      ConfigurationClassPostProcessor實作了beandefinitionregistrypostprocessor接口,beandefinitionregistryPostProcessor又擴充了beanfactorypostprocessor接口,beanfactorypostprocessor是spring的擴充點之一,configurationclasspostprocessor是spring的重要類
      spring-ioc源碼
      除了注冊configurationclass後置處理器還注冊了其他bean,比如bean後置處理器,也是spring的擴充點之一。至此,執行個體化annotatedbeandefinitionreader reader完畢。
  5. 建立beandefinition掃描器,classpathbeandefinitionscanner
    1. 由于正常方法不會用到annotationconfigapplicationcontext裡的scanner。這裡的canner僅僅是為了程式員手動調用裡面的scan方法
  6. 注冊配置類為beandefinition: register(annotatedclasses)
    spring-ioc源碼
    1.  傳入的參數是數組      
    spring-ioc源碼
    spring-ioc源碼
    spring-ioc源碼
    這裡需要注意的是以正常的方式去注冊配置類,此方法除了第一個參數,其他參數都是預設值。
    1. 通過annotatedGenericBeanDefinition的構造方法,擷取配置的bean定義,在注冊configutationClass後置處理器也是通過構造方法擷取bean定義,隻是通過rootbeandefinition,現在是annotatedGenericBeanDefinition中獲得
    2. 判斷需不需要跳過注冊,spring中有一個@Condition,如果不滿足條件就會跳過這個類注冊
    3. 解析作用域,如果沒設定就預設為單例
    4. 擷取beanname
    5. 解析通用注解,填充到annotatedGenericBeanDefinition,解析的注解為lazy,primary,dependsOn,role,Description
    6. 限定符處理,除了@Qualifier,還有primary,lazy
    7. 把annotatedGenericBeanDefinition資料結構和beanname封裝到一個對象中
    8. 注冊,最終會調用defaultlistableBeanFactory中的registerBeanDefinition方法注冊
    spring-ioc源碼
    spring-ioc源碼
    spring-ioc源碼
  7. refresh()方法
    spring-ioc源碼
    spring-ioc源碼
    spring-ioc源碼
    1. prepareRefresh

      主要是做一些重新整理前的準備工作,和主流程關系不大,主要是儲存容器的啟動時間,啟動标志

    2. configurablelistablebeanfactory beanfactory = obtainFreshBeanFactory()

      和主流程關系不大,主要是把beanfactory取出來,xml模式下到這裡會讀取beanDefinition

    3. prepareBeanFactory

      添加了倆個後置處理器 applicationContextAwareProcessor,applicationListenerDetector,還設定了忽略自動裝配 和允許自動裝配的接口,如果不存在某個bean的時候,spring就自動注冊singleton Bean.以及bean表達式解析器

      spring-ioc源碼
      spring-ioc源碼
      spring-ioc源碼
      主要的操作如下
      1. 設定了類加載器
      2. 設定了bean表達式解析器
      3. 添加了屬性編輯器的支援
      4. 添加了一個後置處理器:applicationContextAwareProcessor,此後置處理器實作了beanpostprocessor接口
      5. 設定了一些忽略自動裝配的接口
      6. 設定了一些允許自動裝配的接口,并且進行了複制操作
      7. 在容器中還沒有xxbean的時候,幫我們注冊beanName為xx的單例bean
    4. postProcessBeanFactory(beanFactory)

      空方法,以後可能會被擴充

    5. invokeBeanFactoryPostProcessors(BeanFactory)
      spring-ioc源碼
      看這個小方法
      spring-ioc源碼
      這裡獲得的是beanFactorypostprocessor,可以手動添加一個後置處理器,而不是交給spring去掃描
      spring-ioc源碼
      隻有這樣,這個集合才不會為空。
      spring-ioc源碼
      spring-ioc源碼
      spring-ioc源碼
      spring-ioc源碼
      spring-ioc源碼
      spring-ioc源碼
      spring-ioc源碼
      spring-ioc源碼
      spring-ioc源碼
      spring-ioc源碼
      首先判斷beanFactory是不是beandefinitionRegistry的執行個體,然後執行一下操作
      1. 定義一個set,裝beanName,然後根據這個set,來判斷後置處理器是否被執行過
      2. 定義倆個list,一個是regular後置處理器,用來裝beanFactory後置處理器,一個是registryProcessors用來裝載beanDefinitionRegistry後置處理器,其中bean定義注冊後置處理器擴充了beanFactory後置處理器,bean定義注冊後置處理器有倆個方法,一個是獨有的後置處理器bean定義注冊方法,一個是父類的後置處理器beanFactory方法
      3. 循環傳入的beanfactory後置處理器,一般情況下都是空的,唯有手動加入beanFactory後置處理器。才有資料。因為bean定義注冊後置處理器擴充了beanFactory後置處理器,是以這裡要判斷是不是bean定義注冊後置處理器,是的話就執行後置處理器bean定義注冊方法,然後把對象封裝到registryprocessory中,不是的話就封裝到regular後置處理器中
      4. 定義一個臨時變量currentRegistryprocessors,用來裝bean定義注冊後置處理器
      5. getBeanNamesforType,通過類型找到beannames,從beanDefinitionNames去找,舉個例子,如果這裡傳入了beanDefinitionregistryPostProcessor.class,就會找到類型為該類型的後置處理器。并且指派給postProcessoryNames.一般情況下隻會找到一個,就是internalconfigurationAnnotationProcessor,也就是confurationAnnotationProcess.如果你實作了bean定義後置處理器接口,也打上了@Component注解,但是在這裡沒有擷取到,因為spring掃描實在configurationClass後置處理器類中完成的,也就是下面的invokeBeanDefinitionRegistry後置處理器方法。
      6. 循環postProcessorNames,是internalConfigurationAnnotationProcessor,判斷此後置處理器是否實作了priorityordered接口。如果實作了,就把它添加到currentregistryProcessors中。再放入processedBeans,代表這個後置處理器已經被處理過了。
        spring-ioc源碼
      7. 進行排序,priorityOrdered是一個排序接口,如果實作了它說明後置處理器是有順序的,要排序,目前隻有configurationClass後置處理器
      8. 把currentRegistyprocessors合并到registryprocessors,因為一開始spirng隻會執行beanDefinitionRegistry後置處理器的獨有方法,而不執行父類方法,是以需要把這些後置處理器放入到一個集合中,後續統一執行beanFactoryProcessor接口中的方法。
      9. 可以了解為執行currentRegistryprocessors中的中的ConfigurationClass後置處理器中的postProcessBeanDefinitionRegistry方法,這裡展現了spring的設計思想,熱插拔。spring很多東西交給插件去做,如果不想用,就不添加
        spring-ioc源碼
      10. 清空currentRegistryProcessors,因為currentRegistryProcessors是一個臨時變量,完成了目前的任務,是以清空,後面還會用到
      11. 再次根據bean定義注冊後置處理器獲得beanname,然後進行循環,看這個後置處理器是否被執行過了,如果沒有執行,也實作了order接口,就把此後置處理器推送到currentRegistryprocessors和processedBean中。這裡就可以獲得我們定義的并且搭上了@Component注解的後置處理器。因為spring完成了掃描,但是需要注意由于ConfigurationClassPostProcessors上面已經被執行了,雖然可以通過getBeanNameForType獲得,但是并不會加入到currentRegistryProcessors和processedBeanas.
      12. 處理排序
      13. 合并processors,合并的理由和上面一樣
      14. 執行我們自定義的beandefinitionregistry後置處理器
      15. 臨時清空變量
      16. 在上面的方法中,僅僅是執行了實作orderer接口的bean定義注冊後置處理器,這裡是執行沒有實作ordered接口的bean定義注冊後置處理器。
      17. 上面的代碼時執行子類獨有的方法,這裡需要把父類的方法也執行一次
      18. 執行regular後置處理器中的後置處理器方法,需要注意的時,在一幫情況下,regular後置處理器,隻有在外面手動添加beanFactory後置處理器才有資料。
      19. 查找實作了beanFactory後置處理器,并且執行了後置處理器中的方法。如下圖
        spring-ioc源碼
        spring-ioc源碼
        spring-ioc源碼
        spring-ioc源碼
        spring-ioc源碼
        spring-ioc源碼
        1. 擷取所有的beanName,放入candidateNames數組      
        1. 循環candidateNames數組,根據beanName擷取beanDefinition,判斷是否被處理過了。
        2. 判斷是否是配置類,如果是,加入到configCandidates數組,在判斷的時候還會标記配置類屬于full還是lite配置類。

        3.1 當我們注冊配置類的時候,可以加@Configuration,也可以加@Component,@ComponentScan,@Import,@ImportResource等注解,spring稱這種配置為lite配置類,如果加了@Configuration就稱之為full配置類。

        3.2 如果我們注冊了lite配置類,getBean時會發現就是原本的配置類,如果我們注冊full配置類,getbean時是一個cglib代理類。

        spring-ioc源碼
        spring-ioc源碼
        3.3 寫一個A類,其中一個構造方法是列印”你好“ ,在一個配置類,有倆個被@Bean注解的方法,其中一個方法return new A(),命名為getA(),另一個方法調用getA(),如果配置類是lite配置類,會發現列印倆次你好,也就是說A類被new了倆次,如果配置類是Full類,會發現隻列印一次”你好“,也就是說A類隻被new了一次,因為這個類被cglib代理了,方法已經被改寫。
spring-ioc源碼
spring-ioc源碼
spring-ioc源碼
spring-ioc源碼
spring-ioc源碼
  1. 遞歸處理内部類,一般不會使用内部類。
  2. 處理@PropertySource注解,@ProertySource注解用來加載properties檔案
  3. 獲得componentscan注解具體的内容,componentscan注解出了最常用的basePackage之外,還有includeFilters,excludeFilters等
  4. 判斷有沒有被@ComponentScans标記,或者被@Condition條件帶過,如果滿足條件的話,進入if,進行如下操作。
  1. 執行掃描操作,吧掃描出來的放入set.
  2. 循環set,判斷是否是配置類,是的話遞歸調用parse方法,因為被掃描出來的類,還是一個配置類,有@ComponentScan注解,或者其中有被@Bean标記的方法,等等,是以需要被再次解析。
  1. 處理@Import注解,他有三種情況,一種是import普通類,一種是importSelector,還有一種是 importBeanDefinitionRegistrar,getImports(sourceClass)是獲得import的内容,傳回的是一個set
  2. 處理@ImportResource注解
  3. 處理@Bean的方法,可以看到獲得了帶有@Bean的方法後,不是馬上轉化稱beanDefinition,而是先用一個set接受
spring-ioc源碼
spring-ioc源碼
spring-ioc源碼
spring-ioc源碼
  1. 定義了一個掃描器scanner,正常方法中,實際上執行掃描隻會是這裡的scanner對象
  2. 處理includeFilters,就是把規則添加到scanner
  3. 處理excludeFilters,就是把規則添加到scanner.
  4. 解析basepackages,獲得需要掃描哪些包。
  5. 添加一個預設的排除規則,拍出自己
  6. 執行掃描

這裡有一個補充說明,添加規則的時候,隻是把具體的規則放入規則類的集合中去,規則類是一個函數式接口,之定義了一個虛方法的接口被稱為函數式接口,這裡隻是把規則放進去,并沒有真正執行這些規則。

spring-ioc源碼
spring-ioc源碼

因為basePackages可能有多個,是以需要循環處理,最終會進行bean的注冊,看一下findCandidateComponents

spring-ioc源碼

spring支援component索引技術,需要引入一個元件,大部分項目沒引進,索引會進入scanCandidateComponents方法

spring-ioc源碼
spring-ioc源碼
spring-ioc源碼
spring-ioc源碼
spring-ioc源碼
spring-ioc源碼
spring-ioc源碼
spring-ioc源碼

直到這裡,才把configurationClassPostProcessor中的processConfigBeanDefinitions方法簡單過一下,這裡隻會解析@Import的bean,不會注冊。

processConfigBeanDefinitions是BeanDefinitionRegistryPostProcessor接口中的方法,beanDefinitionRegistry後置處理器擴充了beanfactoryPostProcessor.postProcessBeanFactory是判斷配置類是lite還是full,如果是full就會被cglib代理,目的是保證bean的作用域

spring-ioc源碼

總結:configurationClassPostProcessor中的processConfigBeanDefinitions主要是完成掃描,最終注冊我們定義的bean.

6.6 registerBeanPostProcessors(beanFactory)

執行個體化和注冊beanfactory中擴充了beanPostProcessor的bean.

例如autowriedAnnotationBeanPostProcessor(處理被@Autowired修飾的bean并注入)

requiredAnnotataionBeanPostProcessor(處理被@Requied注解修飾的方法)

commonAnnotationBeanPostProcessor(處理@PreDestory,@Resource等多個注解的作用)等

spring-ioc源碼

6.7 initMessageSource()

初始國際化資源處理器

6.8 initapplicationEventMulticaster()

//建立事件多點傳播器

spring-ioc源碼

6.9 onRefresh()

模闆方法,在容器重新整理的時候可以自定義邏輯,不同的spring容器做不同的事情

6.10 registerListeners()

注冊監聽器,廣播early application events

6.11 finishBeanfactoryInitialization(Beanfactory)

執行個體化所有剩餘的單例,懶加載除外

比如invokeBeanfactorypostprocessorts方法中根據注解解析出來的的類,在這個時候都會被初始化。

執行個體化的過程各種beanpostProcessor開始起作用。

spring-ioc源碼

上面這個方法是執行個體化懶加載單例bean的,也就是我們建立的bean

spring-ioc源碼

再看finishBeanFactoryInitialization這個方法,裡面有一個beanFactory.preInstantiateSingletons()方法,頂進去之後一個接口,它的實作類是defaultListableBeanFactory,找到裡面的getBean方法,這個方法 有一個分支,如果bean是fanctoryBean,對應處理。。。。如果bean不是factoryBean,對應處理。。。。不過不管是不是fanctoryBean最終都會調用getBean方法,直接調用doGetBean.

spring-ioc源碼
spring-ioc源碼

這裡面有一個createBean方法,有一個實作類為abstractAutowireCapableBeanFactory

spring-ioc源碼

建立執行個體

spring-ioc源碼

填充屬性

spring-ioc源碼
spring-ioc源碼

aware系列接口回調

spring-ioc源碼
spring-ioc源碼
spring-ioc源碼
spring-ioc源碼

springBean的生命周期

網上流傳的一段

  1. 執行個體化bean對象,沒有屬性,
  2. 填充屬性
  3. 如果bean實作了beanNameaware接口,調用setBeanName方法
  4. 如果bean實作了beanClassLoaderAware接口,則調用setBeanClassLoader方法
  5. 如果bean實作了beanFactoryAware接口,調用setBeanFactory方法
  6. 調用beanpostProccessor的postProcessBeforeInitialization方法。
  7. 如果bean實作了initializingBean接口,調用afterPropertiesSet方法
  8. 如果bean定義了Initializating接口,調用bean的init-method
  9. 調用beanpostProcessor的postProcessAfterInitialization方法。進行到這裡,bean已經準備就緒,停留在應用的上下文中,知道被銷毀
  10. 如果上下文被銷毀了,如果bean實作了disposableBean接口,則調用destory方法,如果bean定義了destory-method聲明了銷毀方法也會被調用
為了驗證上面的邏輯,可以做個試驗: 

首先定義了一個Bean,裡面有各種回調和鈎子,其中需要注意下,我在SpringBean的構造方法中列印了 

studentService,看SpringBean被new的出來的時候,studentService是否被注入了;又在 

setBeanName中列印了studentService,看此時studentService是否被注入了,以此來驗證,Bean是何時 

完成的自動注入的(這個StudentServiceImpl 類的代碼就不貼出來了,無非就是一個最普通的Bean)      
spring-ioc源碼
spring-ioc源碼
spring-ioc源碼
spring-ioc源碼
spring-ioc源碼

6-12 finishRefresh()

refresh做完之後需要做的事情

清楚上下文資源緩存(如掃描中的asm中繼資料)

初始化上下文的生命周期處理器,并重新整理。

釋出contextRefreshedEvent事件并告知對應的applicationListener進行相應的操作

spring-ioc源碼
spring-ioc源碼
spring-ioc源碼
spring-ioc源碼

2.使用時間廣播器廣播事件到相應的監聽器multicastEvent

spring-ioc源碼

3.2調用監聽器invokeListener

spring-ioc源碼
spring-ioc源碼

這樣,當spring執行到finishrefresh方法時,就會将contextrefreshedEvent事件推送到myrefreshedListener中。跟contextRefreshedEvent相似的還有::ContextStartedEvent、ContextClosedEvent、

ContextStoppedEvent,有興趣的可以自己看看這幾個事件的使用場景。當然也可以自定義監聽事件,隻需要繼承applicationContextEvent抽象類即可

不會,我可以學;落後,我可以追趕;跌倒,我可以站起來!