SpringIOC?BeanDefinition的加載流程詳解
一.前言
這一篇來(lái)看看 SpringIOC 里面的一個(gè)細(xì)節(jié)點(diǎn) , 來(lái)簡(jiǎn)單看看 BeanDefinition 這個(gè)對(duì)象 , 以及有沒(méi)有辦法對(duì)其進(jìn)行定制.
CASE 備份 : ?? gitee.com/antblack/ca…
二. BeanDefinition 的體系
2.1 體系概覽

這里面需要關(guān)注的幾個(gè)類分別為 :
- BeanDefinition 接口 : 頂層接口 , 抽象了Bean加載的方法
- AbstractBeanDefinition : 提供了多數(shù)方法的默認(rèn)實(shí)現(xiàn)
- RootBeanDefinition : Spring BeanFactory 運(yùn)行時(shí)統(tǒng)一的 BeanDefinition 視圖
- GenericBeanDefinition : 編程方式注冊(cè) BeanDefinition 的首選類
- ChildBeanDefinition : 可繼承BeanDefinition
下面來(lái)解釋一下這里面說(shuō)的一些概念 :
什么叫統(tǒng)一視圖 ?
稍微從跟蹤一下源碼就能發(fā)現(xiàn) , 從 xml 或者 JavaConfig 以及 Spring 默認(rèn)加載的Bean配置類 ,最終都會(huì)被修飾為 RootBeanDefinition
GenericBeanDefinition 怎么用 ?
GenericBeanDefinition 是通過(guò)編程方式注入的 BeanDefinition 所對(duì)應(yīng)的類 ,通常都是該類的子類 , 包括非Spring 的 ConfigBean 和 ServiceBean
ChildBeanDefinition 又做了什么 ?
一種可以繼承 parent 配置的 BeanDefinition , 在加載環(huán)節(jié)中會(huì)通過(guò) AbstractBeanFactory#getMergedLocalBeanDefinition() 來(lái)將 child 和 parent bean definition 進(jìn)行合并。
- BeanDefinition 進(jìn)行 merge 操作時(shí),會(huì)將 child 的屬性與 parent 的屬性進(jìn)行合并,當(dāng)有相同屬性時(shí),以 child 的為準(zhǔn)
- 如果是 Map 形式的配置 , 會(huì)取并集
2.2 BeanDefinition 的作用
- 存儲(chǔ)屬性 : 基于接口 AttributeAccessor 實(shí)現(xiàn)
- 存儲(chǔ)元數(shù)據(jù)配置 : 基于 BeanMetadataElement 實(shí)現(xiàn)
- 描述類的信息 : 包括Bean名稱 , Primary 屬性 , priority 配置 等等
- Bean 的加載 : 例如 getBeansOfType , getBean 等等
總結(jié)其實(shí)就是一句話 : BeanDefinition 主要承載了Bean的元數(shù)據(jù)信息 ,同時(shí)描述了Bean在Spring體系中的加載方式 , 容器通過(guò) BeanDefinition 中的配置來(lái)加載一個(gè)Bean
三. BeanDefinition 的載入
3.1 載入的入口
S1 : 啟動(dòng)配置類的載入
Spring 中第一個(gè)載入的 BeanDefinition 即為 RootBeanDefinition , 主要通過(guò) AnnotationConfigUtils # registerAnnotationConfigProcessors 方法進(jìn)行加載
在這個(gè)環(huán)節(jié)中會(huì)通過(guò)加載的方式分別載入多個(gè)不同的 RootBeanDefinition , 這里是 Contain 關(guān)系 :
// internalConfigurationAnnotationProcessor
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
// 構(gòu)建對(duì)應(yīng)的 PostProcessor 并且載入
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// internalAutowiredAnnotationProcessor
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
//.....
}
在這個(gè)環(huán)節(jié)中 , 基本上都是在通過(guò) registerPostProcessor 來(lái)注冊(cè)各類加載類 , 我把這些看成根類 .
這些類通常為 Spring 進(jìn)行服務(wù) , 用來(lái)配置各類信息和加載封裝 Bean
S2 : 普通配置類的載入
普通類的載入包括 SpringApplication 和一些自定義的個(gè)人配置類 , 這些類主要為了對(duì)非 Spring 的組件進(jìn)行注冊(cè) , 配置 , 注入等操作
這一類通常通過(guò) registerBean 來(lái)實(shí)現(xiàn)Bean的注冊(cè) , 注冊(cè)的入口也很多 :
包括 AnnotatedBeanDefinitionReader 和 ConfigurationClassPostProcessor等, 不難發(fā)現(xiàn)這一類 BeanDefinition 通常都是由 RootBeanDefinition 裝載的類進(jìn)行載入的
通常注冊(cè)出來(lái)的對(duì)象也為 AnnotatedGenericBeanDefinition 和 GenericBeanDefinition 的子類等
3.2 保存的邏輯
BeanDefinition 會(huì)在 DefaultListableBeanFactory # registerBeanDefinition 中進(jìn)行注冊(cè).
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
// S1 : 會(huì)對(duì) BeanDefinition 進(jìn)行校驗(yàn) , 主要是MethodOverrides和FactoryMethodName不能同時(shí)存在
// -- 工廠方法必須創(chuàng)建具體的 Bean 實(shí)例 , 而 methodOverrides 會(huì)創(chuàng)建代理類且進(jìn)行增強(qiáng)
// -- 也就是說(shuō) 工廠需要實(shí)例 , 不能是代理類
if (beanDefinition instanceof AbstractBeanDefinition) {
((AbstractBeanDefinition) beanDefinition).validate();
}
// S2 : 判斷 BeanDefinition 是否已經(jīng)存在
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
// 是否允許同名Bean重寫 , 因?yàn)榇颂幰呀?jīng)存在一個(gè)了 , 不能重寫則直接異常
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// 角色比較 ,只打日志
// ROLE_APPLICATION / ROLE_SUPPORT / ROLE_INFRASTRUCTURE
}else if (!beanDefinition.equals(existingDefinition)) {
// 判斷是否為同一對(duì)象
}
// 以上主要是打log , 這里如果允許覆蓋則直接覆蓋了
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
// 判斷該Bean是否已經(jīng)開始初始化
if (hasBeanCreationStarted()) {
// 如果已經(jīng)開始 , 需要對(duì) Map 上鎖后再處理
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
// 省略一些更新操作
}
}
else {
// 沒(méi)有加載時(shí)的載入
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
// 如果Bean已經(jīng)存在或已經(jīng)開始加載了 , 這個(gè)時(shí)候時(shí)需要進(jìn)行銷毀操作的
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
protected void resetBeanDefinition(String beanName) {
// S1 : 如果已經(jīng)創(chuàng)建 ,需要清空 Merge BeanDefinition
clearMergedBeanDefinition(beanName);
// S2 : 銷毀 Bean
destroySingleton(beanName);
// S3 : 調(diào)用 PostProcessors 重置處理器進(jìn)行處理
for (BeanPostProcessor processor : getBeanPostProcessors()) {
if (processor instanceof MergedBeanDefinitionPostProcessor) {
((MergedBeanDefinitionPostProcessor) processor).resetBeanDefinition(beanName);
}
}
// S4 : 如果該 BeanDefinition 是某個(gè)BeanDefinition 的Parent , 則需要同步處理
for (String bdName : this.beanDefinitionNames) {
if (!beanName.equals(bdName)) {
BeanDefinition bd = this.beanDefinitionMap.get(bdName);
if (bd != null && beanName.equals(bd.getParentName())) {
resetBeanDefinition(bdName);
}
}
}
}
3.3 使用的方式
BeanDefinition 的批量處理流程也是在 DefaultListableBeanFactory 中進(jìn)行的
public void preInstantiateSingletons() throws BeansException {
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// 對(duì)所有的 BeanDefinition 進(jìn)行循環(huán)處理
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
// 排除掉懶加載的Bean
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
// 此處省略工廠類的判斷及處理 ...
// 進(jìn)入Bean獲取邏輯
getBean(beanName);
}
}
//.....
}
總結(jié)
分析 BeanDefinition 不是這階段的主要目的 , 后續(xù)會(huì)有幾篇應(yīng)用的文章來(lái)著重思考下如何進(jìn)行業(yè)務(wù)定制
其實(shí)寫源碼文章是最輕松的 , 看懂就完事了 , 而寫定制或者業(yè)務(wù) , 往往寫著寫著發(fā)現(xiàn)有地方?jīng)]搞懂 , 就需要回頭繼續(xù)看這個(gè)點(diǎn) , 難度要大得多.......
附錄 : BeanDefinition 功能一覽

以上就是SpringIOC BeanDefinition的加載流程詳解的詳細(xì)內(nèi)容,更多關(guān)于SpringIOC BeanDefinition加載的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java通過(guò)socket客戶端保持連接服務(wù)端實(shí)現(xiàn)代碼
這篇文章主要介紹了Java通過(guò)socket客戶端保持連接服務(wù)端實(shí)現(xiàn)代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11
詳解Spring Aop實(shí)例之AspectJ注解配置
本篇文章主要介紹了詳解Spring Aop實(shí)例之AspectJ注解配置,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-04-04
Java實(shí)現(xiàn)把窗體隱藏到系統(tǒng)托盤方法
這篇文章主要介紹了Java實(shí)現(xiàn)把窗體隱藏到系統(tǒng)托盤方法,本文直接給出核心功能代碼,需要的朋友可以參考下2015-05-05
通過(guò)Java實(shí)現(xiàn)中文分詞與文本關(guān)鍵詞提取
這篇文章主要為大家詳細(xì)介紹了如何利用Java實(shí)現(xiàn)中文分詞以及文本關(guān)鍵詞提取功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)學(xué)習(xí)2023-06-06
java selenium 常見web UI 元素操作及API使用
本文主要介紹java selenium 常見web UI 元素操作,這里幫大家整理了相關(guān)資料并附示例代碼,有需要的小伙伴可以參考下2016-08-08
淺析SpringBoot2.4 靜態(tài)資源加載問(wèn)題
這篇文章主要介紹了SpringBoot2.4 靜態(tài)資源加載問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01
SpringBoot使用validation-api實(shí)現(xiàn)參數(shù)校驗(yàn)的示例
這篇文章主要介紹了SpringBoot使用validation-api實(shí)現(xiàn)參數(shù)校驗(yàn)的示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
啟動(dòng)Tomcat時(shí)出現(xiàn)大量亂碼的解決方法
今天給大家?guī)?lái)的是關(guān)于Java的相關(guān)知識(shí),文章圍繞著啟動(dòng)Tomcat時(shí)出現(xiàn)大量亂碼的解決方法展開,文中有非常詳細(xì)的介紹及圖文示例,需要的朋友可以參考下2021-06-06

