Spring最重要的功能就是幫助程序員創建對象(也就是IOC),而啓動Spring就是爲創建Bean對象做準備,所以我們先明白Spring到底是怎麼去創建Bean的,也就是先弄明白Bean的生命週期。
Bean的生命週期就是指:在Spring中,一個Bean是如何生成的,如何銷燬的
Bean生命週期流程圖:https://www.processon.com/view/link/5f8588c87d9c0806f27358c1
附帶資料JFR介紹:https://zhuanlan.zhihu.com/p/122247741
Bean的生成過程
1. 生成BeanDefinition
Spring啓動的時候會進行掃描,會先調用 org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents(String basePackage)
掃描某個包路徑,並得到BeanDefinition的Set集合。
關於Spring啓動流程,後續會單獨的課詳細講,這裏先講一下Spring掃描的底層實現:
Spring掃描底層流程:https://www.processon.com/view/link/61370ee60e3e7412ecd95d43
- 首先,通過ResourcePatternResolver獲得指定包路徑下的所有
.class
文件(Spring源碼中將此文件包裝成了Resource對象) - 遍歷每個Resource對象
- 利用MetadataReaderFactory解析Resource對象得到MetadataReader(在Spring源碼中MetadataReaderFactory具體的實現類爲CachingMetadataReaderFactory,MetadataReader的具體實現類爲SimpleMetadataReader)
- 利用MetadataReader進行excludeFilters和includeFilters,以及條件註解@Conditional的篩選(條件註解並不能理解:某個類上是否存在@Conditional註解,如果存在則調用註解中所指定的類的match方法進行匹配,匹配成功則通過篩選,匹配失敗則pass掉。)
- 篩選通過後,基於metadataReader生成ScannedGenericBeanDefinition
- 再基於metadataReader判斷是不是對應的類是不是接口或抽象類
- 如果篩選通過,那麼就表示掃描到了一個Bean,將ScannedGenericBeanDefinition加入結果集
MetadataReader表示類的元數據讀取器,主要包含了一個AnnotationMetadata,功能有
- 獲取類的名字、
- 獲取父類的名字
- 獲取所實現的所有接口名
- 獲取所有內部類的名字
- 判斷是不是抽象類
- 判斷是不是接口
- 判斷是不是一個註解
- 獲取擁有某個註解的方法集合
- 獲取類上添加的所有註解信息
- 獲取類上添加的所有註解類型集合
值得注意的是,CachingMetadataReaderFactory解析某個.class文件得到MetadataReader對象是利用的ASM技術,並沒有加載這個類到JVM。並且,最終得到的ScannedGenericBeanDefinition對象,beanClass屬性存儲的是當前類的名字,而不是class對象。(beanClass屬性的類型是Object,它即可以存儲類的名字,也可以存儲class對象)
最後,上面是說的通過掃描得到BeanDefinition對象,我們還可以通過直接定義BeanDefinition,或解析spring.xml文件的<bean/>,或者@Bean註解得到BeanDefinition對象。(後續課程會分析@Bean註解是怎麼生成BeanDefinition的)。
2. 合併BeanDefinition
通過掃描得到所有BeanDefinition之後,就可以根據BeanDefinition創建Bean對象了,但是在Spring中支持父子BeanDefinition,和Java父子類類似,但是完全不是一回事。
父子BeanDefinition實際用的比較少,使用是這樣的,比如:
<bean id="parent" class="com.zhouyu.service.Parent" scope="prototype"/> <bean id="child" class="com.zhouyu.service.Child"/>
這麼定義的情況下,child是單例Bean。
<bean id="parent" class="com.zhouyu.service.Parent" scope="prototype"/> <bean id="child" class="com.zhouyu.service.Child" parent="parent"/>