spring-ioc源碼
需要解決的問題
- beanfactory和factorybean的差別
- beanfactorypostprocessor在spring中的作用
- springioc的加載過程
- bean的生命周期
- spring中有哪些擴充接口及調用時機
大綱
1. 主要流程-springioc的加載過程
- 執行個體化容器AnnotationConfigApplicationContext
- 執行個體化工廠DefaultListAbleBeanFactory
- 執行個體化建立beanDefination讀取器 annotatedBeandefinationReader
- 建立beandefination掃描器:classPathBeanDefiantionScanner
- 注冊配置類為beanDefination: register方法,annotatedClasses
- refresh
- invokeBeanfacorypostProcessors(beanfactory)
- finishBeanfactoryinitialization(beanfactory)
2. 主要流程-spring bean的生命周期
springioc的加載過程
- 首先準備個例子如下(插入圖檔)
- 2.springioc的容器加載流程
-
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
annotationConfigApplicationContext的關系圖如下
建立annotationConfigApplicationContext對象
首先看這個構造方法
-
3.4 如果沒有配置類會位元組傳回
3.5 處理排序
3.6 解析配置類,可能是full類,也可能是lite類
3.7 在第六步的時候隻是注冊了部分Bean,但是如@Import@Bean,是沒有被注冊的。
因為可以有多個配置類,是以需要循環處理。我們的配置類的BeanDefinition是AnnotatedBeanDefinition的執行個體,是以會進入第一個
if
重點在于doProcessConfigurationClass方法,需要特别注意,最後一段代碼,會把configClass放于一個map,
- 這是一個有參構造方法,可以接受多個配置類,不過一般情況隻傳入一個配置類。
-
這個配置類有倆種情況,一種是傳統意義上的帶@Configutarion注解的配置類,還有一種是帶@Component,@import,@importResource等注解的配置類,在spring中前者被稱為full配置類,後者叫lite配置類,也叫普通bean
檢視這個this()方法都幹了什麼,如圖
spring-ioc源碼 首先無參構造方法中就是對讀取器read和掃描器進行了執行個體化,reader的類型是annotatedbeandefinitionReader,可以看出他是一個 bean定義讀取器,scanner的類型是classpathbeandefinitionscanner,它僅僅是外面手動調用.scan方法,spring-ioc源碼 - 執行個體化工廠:defaultListableBeanFactory DefaultListableBeanFactory的關系圖
spring-ioc源碼 defaultlistablebeanfactory是bean的工廠,是用來生産和獲得bean。spring-ioc源碼 -
執行個體化建beanDefinition讀取器:annotatedbeandefinitionReader
主要負責倆件事
- 注冊内置beanPostprocrssor
- 注冊相關的beandefinition
spring-ioc源碼 回到annotationconfigapplicationcontext的無參構造方法,看annotatedbeandefinitionreader
這裡beandefinitionregistry是annotationconfigapplicationcontext的執行個體,這裡調用其他類構造方法spring-ioc源碼 spring-ioc源碼 spring-ioc源碼 這個方法就是注冊spring的内置的多個beanspring-ioc源碼 - 判斷容器内是否存在configurationclasspostprocessor bean
- 如果不存在,就通過rootBeanDefinition的構造方法獲得configurationclasspostprocessor的beandefinition,rootbeandefinition是beandefinition的子類
- 執行registerpostprocessor方法,注冊bean
spring-ioc源碼 - beanmetadataelement接口,beandefinition中繼資料,傳回該bean的來源
-
attributeaccessor接口,提供對beandefinition屬性操作能力
spring-ioc源碼 - 他是用來描述bean,裡面存放着關于bean的一些列資訊,比如bean的作用域,bean所對應的class,是否懶加載,是否primary等等。
- registerpostprocessor 這方法為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完畢。spring-ioc源碼
- 注冊内置beanPostprocrssor
- 建立beandefinition掃描器,classpathbeandefinitionscanner
- 由于正常方法不會用到annotationconfigapplicationcontext裡的scanner。這裡的canner僅僅是為了程式員手動調用裡面的scan方法
- 注冊配置類為beandefinition: register(annotatedclasses)
spring-ioc源碼 1. 傳入的參數是數組
spring-ioc源碼 spring-ioc源碼 這裡需要注意的是以正常的方式去注冊配置類,此方法除了第一個參數,其他參數都是預設值。spring-ioc源碼 - 通過annotatedGenericBeanDefinition的構造方法,擷取配置的bean定義,在注冊configutationClass後置處理器也是通過構造方法擷取bean定義,隻是通過rootbeandefinition,現在是annotatedGenericBeanDefinition中獲得
- 判斷需不需要跳過注冊,spring中有一個@Condition,如果不滿足條件就會跳過這個類注冊
- 解析作用域,如果沒設定就預設為單例
- 擷取beanname
- 解析通用注解,填充到annotatedGenericBeanDefinition,解析的注解為lazy,primary,dependsOn,role,Description
- 限定符處理,除了@Qualifier,還有primary,lazy
- 把annotatedGenericBeanDefinition資料結構和beanname封裝到一個對象中
- 注冊,最終會調用defaultlistableBeanFactory中的registerBeanDefinition方法注冊
spring-ioc源碼 spring-ioc源碼 spring-ioc源碼 - refresh()方法
spring-ioc源碼 spring-ioc源碼 spring-ioc源碼 -
prepareRefresh
主要是做一些重新整理前的準備工作,和主流程關系不大,主要是儲存容器的啟動時間,啟動标志
-
configurablelistablebeanfactory beanfactory = obtainFreshBeanFactory()
和主流程關系不大,主要是把beanfactory取出來,xml模式下到這裡會讀取beanDefinition
-
prepareBeanFactory
添加了倆個後置處理器 applicationContextAwareProcessor,applicationListenerDetector,還設定了忽略自動裝配 和允許自動裝配的接口,如果不存在某個bean的時候,spring就自動注冊singleton Bean.以及bean表達式解析器
spring-ioc源碼 spring-ioc源碼 主要的操作如下spring-ioc源碼 - 設定了類加載器
- 設定了bean表達式解析器
- 添加了屬性編輯器的支援
- 添加了一個後置處理器:applicationContextAwareProcessor,此後置處理器實作了beanpostprocessor接口
- 設定了一些忽略自動裝配的接口
- 設定了一些允許自動裝配的接口,并且進行了複制操作
- 在容器中還沒有xxbean的時候,幫我們注冊beanName為xx的單例bean
-
postProcessBeanFactory(beanFactory)
空方法,以後可能會被擴充
- invokeBeanFactoryPostProcessors(BeanFactory) 看這個小方法
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的執行個體,然後執行一下操作spring-ioc源碼 - 定義一個set,裝beanName,然後根據這個set,來判斷後置處理器是否被執行過
- 定義倆個list,一個是regular後置處理器,用來裝beanFactory後置處理器,一個是registryProcessors用來裝載beanDefinitionRegistry後置處理器,其中bean定義注冊後置處理器擴充了beanFactory後置處理器,bean定義注冊後置處理器有倆個方法,一個是獨有的後置處理器bean定義注冊方法,一個是父類的後置處理器beanFactory方法
- 循環傳入的beanfactory後置處理器,一般情況下都是空的,唯有手動加入beanFactory後置處理器。才有資料。因為bean定義注冊後置處理器擴充了beanFactory後置處理器,是以這裡要判斷是不是bean定義注冊後置處理器,是的話就執行後置處理器bean定義注冊方法,然後把對象封裝到registryprocessory中,不是的話就封裝到regular後置處理器中
- 定義一個臨時變量currentRegistryprocessors,用來裝bean定義注冊後置處理器
- getBeanNamesforType,通過類型找到beannames,從beanDefinitionNames去找,舉個例子,如果這裡傳入了beanDefinitionregistryPostProcessor.class,就會找到類型為該類型的後置處理器。并且指派給postProcessoryNames.一般情況下隻會找到一個,就是internalconfigurationAnnotationProcessor,也就是confurationAnnotationProcess.如果你實作了bean定義後置處理器接口,也打上了@Component注解,但是在這裡沒有擷取到,因為spring掃描實在configurationClass後置處理器類中完成的,也就是下面的invokeBeanDefinitionRegistry後置處理器方法。
- 循環postProcessorNames,是internalConfigurationAnnotationProcessor,判斷此後置處理器是否實作了priorityordered接口。如果實作了,就把它添加到currentregistryProcessors中。再放入processedBeans,代表這個後置處理器已經被處理過了。
spring-ioc源碼 - 進行排序,priorityOrdered是一個排序接口,如果實作了它說明後置處理器是有順序的,要排序,目前隻有configurationClass後置處理器
- 把currentRegistyprocessors合并到registryprocessors,因為一開始spirng隻會執行beanDefinitionRegistry後置處理器的獨有方法,而不執行父類方法,是以需要把這些後置處理器放入到一個集合中,後續統一執行beanFactoryProcessor接口中的方法。
- 可以了解為執行currentRegistryprocessors中的中的ConfigurationClass後置處理器中的postProcessBeanDefinitionRegistry方法,這裡展現了spring的設計思想,熱插拔。spring很多東西交給插件去做,如果不想用,就不添加
spring-ioc源碼 - 清空currentRegistryProcessors,因為currentRegistryProcessors是一個臨時變量,完成了目前的任務,是以清空,後面還會用到
- 再次根據bean定義注冊後置處理器獲得beanname,然後進行循環,看這個後置處理器是否被執行過了,如果沒有執行,也實作了order接口,就把此後置處理器推送到currentRegistryprocessors和processedBean中。這裡就可以獲得我們定義的并且搭上了@Component注解的後置處理器。因為spring完成了掃描,但是需要注意由于ConfigurationClassPostProcessors上面已經被執行了,雖然可以通過getBeanNameForType獲得,但是并不會加入到currentRegistryProcessors和processedBeanas.
- 處理排序
- 合并processors,合并的理由和上面一樣
- 執行我們自定義的beandefinitionregistry後置處理器
- 臨時清空變量
- 在上面的方法中,僅僅是執行了實作orderer接口的bean定義注冊後置處理器,這裡是執行沒有實作ordered接口的bean定義注冊後置處理器。
- 上面的代碼時執行子類獨有的方法,這裡需要把父類的方法也執行一次
- 執行regular後置處理器中的後置處理器方法,需要注意的時,在一幫情況下,regular後置處理器,隻有在外面手動添加beanFactory後置處理器才有資料。
- 查找實作了beanFactory後置處理器,并且執行了後置處理器中的方法。如下圖
spring-ioc源碼 spring-ioc源碼 spring-ioc源碼 spring-ioc源碼 spring-ioc源碼 spring-ioc源碼 1. 擷取所有的beanName,放入candidateNames數組
- 循環candidateNames數組,根據beanName擷取beanDefinition,判斷是否被處理過了。
- 判斷是否是配置類,如果是,加入到configCandidates數組,在判斷的時候還會标記配置類屬于full還是lite配置類。
3.1 當我們注冊配置類的時候,可以加@Configuration,也可以加@Component,@ComponentScan,@Import,@ImportResource等注解,spring稱這種配置為lite配置類,如果加了@Configuration就稱之為full配置類。
3.2 如果我們注冊了lite配置類,getBean時會發現就是原本的配置類,如果我們注冊full配置類,getbean時是一個cglib代理類。
spring-ioc源碼 3.3 寫一個A類,其中一個構造方法是列印”你好“ ,在一個配置類,有倆個被@Bean注解的方法,其中一個方法return new A(),命名為getA(),另一個方法調用getA(),如果配置類是lite配置類,會發現列印倆次你好,也就是說A類被new了倆次,如果配置類是Full類,會發現隻列印一次”你好“,也就是說A類隻被new了一次,因為這個類被cglib代理了,方法已經被改寫。spring-ioc源碼
-
- 遞歸處理内部類,一般不會使用内部類。
- 處理@PropertySource注解,@ProertySource注解用來加載properties檔案
- 獲得componentscan注解具體的内容,componentscan注解出了最常用的basePackage之外,還有includeFilters,excludeFilters等
- 判斷有沒有被@ComponentScans标記,或者被@Condition條件帶過,如果滿足條件的話,進入if,進行如下操作。
- 執行掃描操作,吧掃描出來的放入set.
- 循環set,判斷是否是配置類,是的話遞歸調用parse方法,因為被掃描出來的類,還是一個配置類,有@ComponentScan注解,或者其中有被@Bean标記的方法,等等,是以需要被再次解析。
- 處理@Import注解,他有三種情況,一種是import普通類,一種是importSelector,還有一種是 importBeanDefinitionRegistrar,getImports(sourceClass)是獲得import的内容,傳回的是一個set
- 處理@ImportResource注解
- 處理@Bean的方法,可以看到獲得了帶有@Bean的方法後,不是馬上轉化稱beanDefinition,而是先用一個set接受
- 定義了一個掃描器scanner,正常方法中,實際上執行掃描隻會是這裡的scanner對象
- 處理includeFilters,就是把規則添加到scanner
- 處理excludeFilters,就是把規則添加到scanner.
- 解析basepackages,獲得需要掃描哪些包。
- 添加一個預設的排除規則,拍出自己
- 執行掃描
這裡有一個補充說明,添加規則的時候,隻是把具體的規則放入規則類的集合中去,規則類是一個函數式接口,之定義了一個虛方法的接口被稱為函數式接口,這裡隻是把規則放進去,并沒有真正執行這些規則。
因為basePackages可能有多個,是以需要循環處理,最終會進行bean的注冊,看一下findCandidateComponents
spring支援component索引技術,需要引入一個元件,大部分項目沒引進,索引會進入scanCandidateComponents方法
直到這裡,才把configurationClassPostProcessor中的processConfigBeanDefinitions方法簡單過一下,這裡隻會解析@Import的bean,不會注冊。
processConfigBeanDefinitions是BeanDefinitionRegistryPostProcessor接口中的方法,beanDefinitionRegistry後置處理器擴充了beanfactoryPostProcessor.postProcessBeanFactory是判斷配置類是lite還是full,如果是full就會被cglib代理,目的是保證bean的作用域
總結:configurationClassPostProcessor中的processConfigBeanDefinitions主要是完成掃描,最終注冊我們定義的bean.
6.6 registerBeanPostProcessors(beanFactory)
執行個體化和注冊beanfactory中擴充了beanPostProcessor的bean.
例如autowriedAnnotationBeanPostProcessor(處理被@Autowired修飾的bean并注入)
requiredAnnotataionBeanPostProcessor(處理被@Requied注解修飾的方法)
commonAnnotationBeanPostProcessor(處理@PreDestory,@Resource等多個注解的作用)等
6.7 initMessageSource()
初始國際化資源處理器
6.8 initapplicationEventMulticaster()
//建立事件多點傳播器
6.9 onRefresh()
模闆方法,在容器重新整理的時候可以自定義邏輯,不同的spring容器做不同的事情
6.10 registerListeners()
注冊監聽器,廣播early application events
6.11 finishBeanfactoryInitialization(Beanfactory)
執行個體化所有剩餘的單例,懶加載除外
比如invokeBeanfactorypostprocessorts方法中根據注解解析出來的的類,在這個時候都會被初始化。
執行個體化的過程各種beanpostProcessor開始起作用。
上面這個方法是執行個體化懶加載單例bean的,也就是我們建立的bean
再看finishBeanFactoryInitialization這個方法,裡面有一個beanFactory.preInstantiateSingletons()方法,頂進去之後一個接口,它的實作類是defaultListableBeanFactory,找到裡面的getBean方法,這個方法 有一個分支,如果bean是fanctoryBean,對應處理。。。。如果bean不是factoryBean,對應處理。。。。不過不管是不是fanctoryBean最終都會調用getBean方法,直接調用doGetBean.
這裡面有一個createBean方法,有一個實作類為abstractAutowireCapableBeanFactory
建立執行個體
填充屬性
aware系列接口回調
springBean的生命周期
網上流傳的一段
- 執行個體化bean對象,沒有屬性,
- 填充屬性
- 如果bean實作了beanNameaware接口,調用setBeanName方法
- 如果bean實作了beanClassLoaderAware接口,則調用setBeanClassLoader方法
- 如果bean實作了beanFactoryAware接口,調用setBeanFactory方法
- 調用beanpostProccessor的postProcessBeforeInitialization方法。
- 如果bean實作了initializingBean接口,調用afterPropertiesSet方法
- 如果bean定義了Initializating接口,調用bean的init-method
- 調用beanpostProcessor的postProcessAfterInitialization方法。進行到這裡,bean已經準備就緒,停留在應用的上下文中,知道被銷毀
- 如果上下文被銷毀了,如果bean實作了disposableBean接口,則調用destory方法,如果bean定義了destory-method聲明了銷毀方法也會被調用
為了驗證上面的邏輯,可以做個試驗:
首先定義了一個Bean,裡面有各種回調和鈎子,其中需要注意下,我在SpringBean的構造方法中列印了
studentService,看SpringBean被new的出來的時候,studentService是否被注入了;又在
setBeanName中列印了studentService,看此時studentService是否被注入了,以此來驗證,Bean是何時
完成的自動注入的(這個StudentServiceImpl 類的代碼就不貼出來了,無非就是一個最普通的Bean)
6-12 finishRefresh()
refresh做完之後需要做的事情
清楚上下文資源緩存(如掃描中的asm中繼資料)
初始化上下文的生命周期處理器,并重新整理。
釋出contextRefreshedEvent事件并告知對應的applicationListener進行相應的操作
2.使用時間廣播器廣播事件到相應的監聽器multicastEvent
3.2調用監聽器invokeListener
這樣,當spring執行到finishrefresh方法時,就會将contextrefreshedEvent事件推送到myrefreshedListener中。跟contextRefreshedEvent相似的還有::ContextStartedEvent、ContextClosedEvent、
ContextStoppedEvent,有興趣的可以自己看看這幾個事件的使用場景。當然也可以自定義監聽事件,隻需要繼承applicationContextEvent抽象類即可
不會,我可以學;落後,我可以追趕;跌倒,我可以站起來!