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

SpringIOC?BeanDefinition的加載流程詳解

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

一.前言

這一篇來(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è)類(lèi)分別為 :

  • BeanDefinition 接口 : 頂層接口 , 抽象了Bean加載的方法
  • AbstractBeanDefinition : 提供了多數(shù)方法的默認(rèn)實(shí)現(xiàn)
  • RootBeanDefinition : Spring BeanFactory 運(yùn)行時(shí)統(tǒng)一的 BeanDefinition 視圖
  • GenericBeanDefinition : 編程方式注冊(cè) BeanDefinition 的首選類(lèi)
  • ChildBeanDefinition : 可繼承BeanDefinition

下面來(lái)解釋一下這里面說(shuō)的一些概念 :

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

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

GenericBeanDefinition 怎么用 ?

GenericBeanDefinition 是通過(guò)編程方式注入的 BeanDefinition 所對(duì)應(yīng)的類(lèi) ,通常都是該類(lèi)的子類(lèi) , 包括非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)
  • 描述類(lèi)的信息 : 包括Bean名稱(chēng) , 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)配置類(lèi)的載入

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è)各類(lèi)加載類(lèi) , 我把這些看成根類(lèi) .

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

S2 : 普通配置類(lèi)的載入

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

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

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

通常注冊(cè)出來(lái)的對(duì)象也為 AnnotatedGenericBeanDefinition 和 GenericBeanDefinition 的子類(lèi)等

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)建代理類(lèi)且進(jìn)行增強(qiáng)
   // -- 也就是說(shuō) 工廠需要實(shí)例 , 不能是代理類(lèi)
   if (beanDefinition instanceof AbstractBeanDefinition) {
       ((AbstractBeanDefinition) beanDefinition).validate();
   }
   // S2 : 判斷 BeanDefinition 是否已經(jīng)存在
   BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
   if (existingDefinition != null) {
      // 是否允許同名Bean重寫(xiě) , 因?yàn)榇颂幰呀?jīng)存在一個(gè)了 , 不能重寫(xiě)則直接異常 
      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)開(kāi)始初始化
      if (hasBeanCreationStarted()) {
         // 如果已經(jīng)開(kāi)始 , 需要對(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)開(kāi)始加載了 , 這個(gè)時(shí)候時(shí)需要進(jìn)行銷(xiāo)毀操作的
   if (existingDefinition != null || containsSingleton(beanName)) {
      resetBeanDefinition(beanName);
   }
}
protected void resetBeanDefinition(String beanName) {
   // S1 : 如果已經(jīng)創(chuàng)建 ,需要清空 Merge BeanDefinition
   clearMergedBeanDefinition(beanName);
   // S2 : 銷(xiāo)毀 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()) {
         // 此處省略工廠類(lèi)的判斷及處理 ...
         // 進(jìn)入Bean獲取邏輯
         getBean(beanName);
      }
   }
    //.....
}

總結(jié)

分析 BeanDefinition 不是這階段的主要目的 , 后續(xù)會(huì)有幾篇應(yīng)用的文章來(lái)著重思考下如何進(jìn)行業(yè)務(wù)定制

其實(shí)寫(xiě)源碼文章是最輕松的 , 看懂就完事了 , 而寫(xiě)定制或者業(yè)務(wù) , 往往寫(xiě)著寫(xiě)著發(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案例講解,本文通過(guò)講解內(nèi)存可見(jiàn)性,Volatile變量來(lái)去詳細(xì)分析該項(xiàng)概念,需要的朋友可以參考下
    2021-07-07
  • Spring中@Qualifier注解的用法

    Spring中@Qualifier注解的用法

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

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

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

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

    通過(guò)Java實(shí)現(xiàn)中文分詞與文本關(guān)鍵詞提取

    這篇文章主要為大家詳細(xì)介紹了如何利用Java實(shí)現(xiàn)中文分詞以及文本關(guān)鍵詞提取功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)學(xué)習(xí)
    2023-06-06
  • java selenium 常見(jiàn)web UI 元素操作及API使用

    java selenium 常見(jiàn)web UI 元素操作及API使用

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

    淺析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)的示例

    這篇文章主要介紹了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)大量亂碼的解決方法

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

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

最新評(píng)論

巴里| 葫芦岛市| 九江市| 定陶县| 阿拉善盟| 封丘县| 兴仁县| 蚌埠市| 兴海县| 邹平县| 临夏市| 晋宁县| 云龙县| 通化县| 阳西县| 老河口市| 文安县| 轮台县| 罗城| 忻州市| 邹平县| 轮台县| 花垣县| 铜梁县| 滨海县| 巴青县| 胶州市| 烟台市| 苏尼特左旗| 万源市| 清涧县| 保定市| 临江市| 黄浦区| 溧阳市| 安吉县| 太原市| 昌江| 清丰县| 临夏市| 增城市|