最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringIOC?BeanDefinition的加載流程詳解

 更新時(shí)間:2022年10月24日 10:15:00   作者:AntBlack  
這篇文章主要為大家介紹了SpringIOC?BeanDefinition的加載流程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

一.前言

這一篇來看看 SpringIOC 里面的一個(gè)細(xì)節(jié)點(diǎn) , 來簡單看看 BeanDefinition 這個(gè)對(duì)象 , 以及有沒有辦法對(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

下面來解釋一下這里面說的一些概念 :

什么叫統(tǒng)一視圖 ?

稍微從跟蹤一下源碼就能發(fā)現(xiàn) , 從 xml 或者 JavaConfig 以及 Spring 默認(rèn)加載的Bean配置類 ,最終都會(huì)被修飾為 RootBeanDefinition

GenericBeanDefinition 怎么用 ?

GenericBeanDefinition 是通過編程方式注入的 BeanDefinition 所對(duì)應(yīng)的類 ,通常都是該類的子類 , 包括非Spring 的 ConfigBean 和 ServiceBean

ChildBeanDefinition 又做了什么 ?

一種可以繼承 parent 配置的 BeanDefinition , 在加載環(huán)節(jié)中會(huì)通過 AbstractBeanFactory#getMergedLocalBeanDefinition() 來將 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體系中的加載方式 , 容器通過 BeanDefinition 中的配置來加載一個(gè)Bean

三. BeanDefinition 的載入

3.1 載入的入口

S1 : 啟動(dòng)配置類的載入

Spring 中第一個(gè)載入的 BeanDefinition 即為 RootBeanDefinition , 主要通過 AnnotationConfigUtils # registerAnnotationConfigProcessors 方法進(jìn)行加載

在這個(gè)環(huán)節(jié)中會(huì)通過加載的方式分別載入多個(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é)中 , 基本上都是在通過 registerPostProcessor 來注冊(cè)各類加載類 , 我把這些看成根類 .

這些類通常為 Spring 進(jìn)行服務(wù) , 用來配置各類信息和加載封裝 Bean

S2 : 普通配置類的載入

普通類的載入包括 SpringApplication 和一些自定義的個(gè)人配置類 , 這些類主要為了對(duì)非 Spring 的組件進(jìn)行注冊(cè) , 配置 , 注入等操作

這一類通常通過 registerBean 來實(shí)現(xiàn)Bean的注冊(cè) , 注冊(cè)的入口也很多 :

包括 AnnotatedBeanDefinitionReaderConfigurationClassPostProcessor等, 不難發(fā)現(xiàn)這一類 BeanDefinition 通常都是由 RootBeanDefinition 裝載的類進(jìn)行載入的

通常注冊(cè)出來的對(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)
   // -- 也就是說 工廠需要實(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 {
         // 沒有加載時(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)用的文章來著重思考下如何進(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 volatile案例講解

    java volatile案例講解

    這篇文章主要介紹了java volatile案例講解,本文通過講解內(nèi)存可見性,Volatile變量來去詳細(xì)分析該項(xiàng)概念,需要的朋友可以參考下
    2021-07-07
  • Spring中@Qualifier注解的用法

    Spring中@Qualifier注解的用法

    但凡有點(diǎn)開發(fā)經(jīng)驗(yàn),多多少少可能都遇到過@Qualifier注解的使用場(chǎng)景,本文主要介紹了Spring中@Qualifier注解的用法,感興趣的可以了解一下
    2023-11-11
  • Java通過socket客戶端保持連接服務(wù)端實(shí)現(xiàn)代碼

    Java通過socket客戶端保持連接服務(wù)端實(shí)現(xiàn)代碼

    這篇文章主要介紹了Java通過socket客戶端保持連接服務(wù)端實(shí)現(xiàn)代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • 詳解Spring Aop實(shí)例之AspectJ注解配置

    詳解Spring Aop實(shí)例之AspectJ注解配置

    本篇文章主要介紹了詳解Spring Aop實(shí)例之AspectJ注解配置,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-04-04
  • Java實(shí)現(xiàn)把窗體隱藏到系統(tǒng)托盤方法

    Java實(shí)現(xiàn)把窗體隱藏到系統(tǒng)托盤方法

    這篇文章主要介紹了Java實(shí)現(xiàn)把窗體隱藏到系統(tǒng)托盤方法,本文直接給出核心功能代碼,需要的朋友可以參考下
    2015-05-05
  • 通過Java實(shí)現(xiàn)中文分詞與文本關(guān)鍵詞提取

    通過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 元素操作及API使用

    本文主要介紹java selenium 常見web UI 元素操作,這里幫大家整理了相關(guān)資料并附示例代碼,有需要的小伙伴可以參考下
    2016-08-08
  • 淺析SpringBoot2.4 靜態(tài)資源加載問題

    淺析SpringBoot2.4 靜態(tài)資源加載問題

    這篇文章主要介紹了SpringBoot2.4 靜態(tài)資源加載問題,本文給大家介紹的非常詳細(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)的示例

    這篇文章主要介紹了SpringBoot使用validation-api實(shí)現(xiàn)參數(shù)校驗(yàn)的示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • 啟動(dòng)Tomcat時(shí)出現(xiàn)大量亂碼的解決方法

    啟動(dòng)Tomcat時(shí)出現(xiàn)大量亂碼的解決方法

    今天給大家?guī)淼氖顷P(guān)于Java的相關(guān)知識(shí),文章圍繞著啟動(dòng)Tomcat時(shí)出現(xiàn)大量亂碼的解決方法展開,文中有非常詳細(xì)的介紹及圖文示例,需要的朋友可以參考下
    2021-06-06

最新評(píng)論

庆安县| 苍梧县| 含山县| 四川省| 孟村| 芦山县| 兴国县| 张家川| 高雄市| 蓬莱市| 治多县| 金门县| 铜梁县| 台南市| 岱山县| 沂源县| 阜南县| 沾化县| 涟源市| 三原县| 闵行区| 荣昌县| 蓝田县| 绍兴市| 岱山县| 沁水县| 克拉玛依市| 望谟县| 大化| 汶川县| 布尔津县| 视频| 康乐县| 江达县| 牟定县| 凤城市| 德钦县| 上饶市| 阿拉尔市| 于田县| 颍上县|