SpringIOC?BeanDefinition的加載流程詳解
一.前言
這一篇來看看 SpringIOC 里面的一個細(xì)節(jié)點 , 來簡單看看 BeanDefinition 這個對象 , 以及有沒有辦法對其進(jìn)行定制.
CASE 備份 : ?? gitee.com/antblack/ca…
二. BeanDefinition 的體系
2.1 體系概覽
這里面需要關(guān)注的幾個類分別為 :
- BeanDefinition 接口 : 頂層接口 , 抽象了Bean加載的方法
- AbstractBeanDefinition : 提供了多數(shù)方法的默認(rèn)實現(xiàn)
- RootBeanDefinition : Spring BeanFactory 運(yùn)行時統(tǒng)一的 BeanDefinition 視圖
- GenericBeanDefinition : 編程方式注冊 BeanDefinition 的首選類
- ChildBeanDefinition : 可繼承BeanDefinition
下面來解釋一下這里面說的一些概念 :
什么叫統(tǒng)一視圖 ?
稍微從跟蹤一下源碼就能發(fā)現(xiàn) , 從 xml 或者 JavaConfig 以及 Spring 默認(rèn)加載的Bean配置類 ,最終都會被修飾為 RootBeanDefinition
GenericBeanDefinition 怎么用 ?
GenericBeanDefinition 是通過編程方式注入的 BeanDefinition 所對應(yīng)的類 ,通常都是該類的子類 , 包括非Spring 的 ConfigBean 和 ServiceBean
ChildBeanDefinition 又做了什么 ?
一種可以繼承 parent 配置的 BeanDefinition , 在加載環(huán)節(jié)中會通過 AbstractBeanFactory#getMergedLocalBeanDefinition() 來將 child 和 parent bean definition 進(jìn)行合并。
- BeanDefinition 進(jìn)行 merge 操作時,會將 child 的屬性與 parent 的屬性進(jìn)行合并,當(dāng)有相同屬性時,以 child 的為準(zhǔn)
- 如果是 Map 形式的配置 , 會取并集
2.2 BeanDefinition 的作用
- 存儲屬性 : 基于接口 AttributeAccessor 實現(xiàn)
- 存儲元數(shù)據(jù)配置 : 基于 BeanMetadataElement 實現(xiàn)
- 描述類的信息 : 包括Bean名稱 , Primary 屬性 , priority 配置 等等
- Bean 的加載 : 例如 getBeansOfType , getBean 等等
總結(jié)其實就是一句話 : BeanDefinition 主要承載了Bean的元數(shù)據(jù)信息 ,同時描述了Bean在Spring體系中的加載方式 , 容器通過 BeanDefinition 中的配置來加載一個Bean
三. BeanDefinition 的載入
3.1 載入的入口
S1 : 啟動配置類的載入
Spring 中第一個載入的 BeanDefinition 即為 RootBeanDefinition , 主要通過 AnnotationConfigUtils # registerAnnotationConfigProcessors 方法進(jìn)行加載
在這個環(huán)節(jié)中會通過加載的方式分別載入多個不同的 RootBeanDefinition , 這里是 Contain 關(guān)系 :
// internalConfigurationAnnotationProcessor if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { // 構(gòu)建對應(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)) { //..... }
在這個環(huán)節(jié)中 , 基本上都是在通過 registerPostProcessor 來注冊各類加載類 , 我把這些看成根類 .
這些類通常為 Spring 進(jìn)行服務(wù) , 用來配置各類信息和加載封裝 Bean
S2 : 普通配置類的載入
普通類的載入包括 SpringApplication
和一些自定義的個人配置類 , 這些類主要為了對非 Spring 的組件進(jìn)行注冊 , 配置 , 注入等操作
這一類通常通過 registerBean 來實現(xiàn)Bean的注冊 , 注冊的入口也很多 :
包括 AnnotatedBeanDefinitionReader
和 ConfigurationClassPostProcessor
等, 不難發(fā)現(xiàn)這一類 BeanDefinition 通常都是由 RootBeanDefinition 裝載的類進(jìn)行載入的
通常注冊出來的對象也為 AnnotatedGenericBeanDefinition 和 GenericBeanDefinition 的子類等
3.2 保存的邏輯
BeanDefinition 會在 DefaultListableBeanFactory # registerBeanDefinition
中進(jìn)行注冊.
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { // S1 : 會對 BeanDefinition 進(jìn)行校驗 , 主要是MethodOverrides和FactoryMethodName不能同時存在 // -- 工廠方法必須創(chuàng)建具體的 Bean 實例 , 而 methodOverrides 會創(chuàng)建代理類且進(jìn)行增強(qiáng) // -- 也就是說 工廠需要實例 , 不能是代理類 if (beanDefinition instanceof AbstractBeanDefinition) { ((AbstractBeanDefinition) beanDefinition).validate(); } // S2 : 判斷 BeanDefinition 是否已經(jīng)存在 BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); if (existingDefinition != null) { // 是否允許同名Bean重寫 , 因為此處已經(jīng)存在一個了 , 不能重寫則直接異常 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)) { // 判斷是否為同一對象 } // 以上主要是打log , 這里如果允許覆蓋則直接覆蓋了 this.beanDefinitionMap.put(beanName, beanDefinition); } else { // 判斷該Bean是否已經(jīng)開始初始化 if (hasBeanCreationStarted()) { // 如果已經(jīng)開始 , 需要對 Map 上鎖后再處理 synchronized (this.beanDefinitionMap) { this.beanDefinitionMap.put(beanName, beanDefinition); // 省略一些更新操作 } } else { // 沒有加載時的載入 this.beanDefinitionMap.put(beanName, beanDefinition); this.beanDefinitionNames.add(beanName); removeManualSingletonName(beanName); } this.frozenBeanDefinitionNames = null; } // 如果Bean已經(jīng)存在或已經(jīng)開始加載了 , 這個時候時需要進(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 是某個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); // 對所有的 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ù)會有幾篇應(yīng)用的文章來著重思考下如何進(jìn)行業(yè)務(wù)定制
其實寫源碼文章是最輕松的 , 看懂就完事了 , 而寫定制或者業(yè)務(wù) , 往往寫著寫著發(fā)現(xiàn)有地方?jīng)]搞懂 , 就需要回頭繼續(xù)看這個點 , 難度要大得多.......
附錄 : BeanDefinition 功能一覽
以上就是SpringIOC BeanDefinition的加載流程詳解的詳細(xì)內(nèi)容,更多關(guān)于SpringIOC BeanDefinition加載的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Springsecurity Oauth2如何設(shè)置token的過期時間
如果用戶在指定的時間內(nèi)有操作就給token延長有限期,否則到期后自動過期,如何設(shè)置token的過期時間,本文就來詳細(xì)的介紹一下2021-08-08SpringBoot關(guān)于自動注入mapper為空的坑及解決
這篇文章主要介紹了SpringBoot關(guān)于自動注入mapper為空的坑及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07Java基礎(chǔ)之多線程方法狀態(tài)和創(chuàng)建方法
Java中可以通過Thread類和Runnable接口來創(chuàng)建多個線程,下面這篇文章主要給大家介紹了關(guān)于Java基礎(chǔ)之多線程方法狀態(tài)和創(chuàng)建方法的相關(guān)資料,需要的朋友可以參考下2021-09-09Netty的Handler鏈調(diào)用機(jī)制及如何組織詳解
這篇文章主要為大家介紹了Netty的Handler鏈調(diào)用機(jī)制及如何組織示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03使用Feign擴(kuò)展包實現(xiàn)微服務(wù)間文件上傳
這篇文章主要為大家詳細(xì)介紹了使用Feign擴(kuò)展包實現(xiàn)微服務(wù)間文件上傳,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-04-04使用Idea maven創(chuàng)建Spring項目過程圖解
這篇文章主要介紹了使用Idea maven創(chuàng)建Spring項目過程圖解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-02-02java實現(xiàn)ArrayList根據(jù)存儲對象排序功能示例
這篇文章主要介紹了java實現(xiàn)ArrayList根據(jù)存儲對象排序功能,結(jié)合實例形式分析了java針對ArrayList的相關(guān)運(yùn)算、排序操作技巧,需要的朋友可以參考下2018-01-01