天天看點

盤點 SpringIOC : BeanDefinition 的加載流程

作者:架構師之道

一 . 前言

這一篇來看看 SpringIOC 裡面的一個細節點 , 來簡單看看 BeanDefinition 這個對象 , 以及有沒有辦法對其進行定制.

二. BeanDefinition 的體系

2.1 體系概覽

盤點 SpringIOC : BeanDefinition 的加載流程

這裡面需要關注的幾個類分别為 :

  • BeanDefinition 接口 : 頂層接口 , 抽象了Bean加載的方法
  • AbstractBeanDefinition : 提供了多數方法的預設實作
  • RootBeanDefinition : Spring BeanFactory 運作時統一的 BeanDefinition 視圖
  • GenericBeanDefinition : 程式設計方式注冊 BeanDefinition 的首選類
  • ChildBeanDefinition : 可繼承BeanDefinition

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

什麼叫統一視圖 ?

稍微從跟蹤一下源碼就能發現 , 從 xml 或者 JavaConfig 以及 Spring 預設加載的Bean配置類 ,最終都會被修飾為 RootBeanDefinition

GenericBeanDefinition 怎麼用 ?

GenericBeanDefinition 是通過程式設計方式注入的 BeanDefinition 所對應的類 ,通常都是該類的子類 , 包括非Spring 的 ConfigBean 和 ServiceBean

ChildBeanDefinition 又做了什麼 ?

一種可以繼承 parent 配置的 BeanDefinition , 在加載環節中會通過 AbstractBeanFactory#getMergedLocalBeanDefinition() 來将 child 和 parent bean definition 進行合并。

  • BeanDefinition 進行 merge 操作時,會将 child 的屬性與 parent 的屬性進行合并,當有相同屬性時,以 child 的為準
  • 如果是 Map 形式的配置 , 會取并集

2.2 BeanDefinition 的作用

  • 存儲屬性 : 基于接口 AttributeAccessor 實作
  • 存儲中繼資料配置 : 基于 BeanMetadataElement 實作
  • 描述類的資訊 : 包括Bean名稱 , Primary 屬性 , priority 配置 等等
  • Bean 的加載 : 例如 getBeansOfType , getBean 等等

總結其實就是一句話 : BeanDefinition 主要承載了Bean的中繼資料資訊 ,同時描述了Bean在Spring體系中的加載方式 , 容器通過 BeanDefinition 中的配置來加載一個Bean

三. BeanDefinition 的載入

3.1 載入的入口

S1 : 啟動配置類的載入

Spring 中第一個載入的 BeanDefinition 即為 RootBeanDefinition , 主要通過 AnnotationConfigUtils # registerAnnotationConfigProcessors 方法進行加載

在這個環節中會通過加載的方式分别載入多個不同的 RootBeanDefinition , 這裡是 Contain 關系 :

// internalConfigurationAnnotationProcessor
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
   // 建構對應的 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)) {
    //.....
}
複制代碼           

在這個環節中 , 基本上都是在通過 registerPostProcessor 來注冊各類加載類 , 我把這些看成根類 .

這些類通常為 Spring 進行服務 , 用來配置各類資訊和加載封裝Bean

S2 : 普通配置類的載入

普通類的載入包括 SpringApplication 和一些自定義的個人配置類 , 這些類主要為了對非 Spring 的元件進行注冊 , 配置 , 注入等操作

這一類通常通過 registerBean 來實作Bean的注冊 , 注冊的入口也很多 :

包括 AnnotatedBeanDefinitionReader 和 ConfigurationClassPostProcessor等, 不難發現這一類 BeanDefinition 通常都是由 RootBeanDefinition 裝載的類進行載入的

通常注冊出來的對象也為 AnnotatedGenericBeanDefinition 和 GenericBeanDefinition 的子類等

3.2 儲存的邏輯

BeanDefinition 會在 DefaultListableBeanFactory # registerBeanDefinition 中進行注冊.

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
      throws BeanDefinitionStoreException {
        
        
   // S1 : 會對 BeanDefinition 進行校驗 , 主要是MethodOverrides和FactoryMethodName不能同時存在
   // -- 工廠方法必須建立具體的 Bean 執行個體 , 而 methodOverrides 會建立代理類且進行增強
   // -- 也就是說 工廠需要執行個體 , 不能是代理類
   if (beanDefinition instanceof AbstractBeanDefinition) {
       ((AbstractBeanDefinition) beanDefinition).validate();
   }

   // S2 : 判斷 BeanDefinition 是否已經存在
   BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
   if (existingDefinition != null) {
      // 是否允許同名Bean重寫 , 因為此處已經存在一個了 , 不能重寫則直接異常 
      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是否已經開始初始化
      if (hasBeanCreationStarted()) {
         // 如果已經開始 , 需要對 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已經存在或已經開始加載了 , 這個時候時需要進行銷毀操作的
   if (existingDefinition != null || containsSingleton(beanName)) {
      resetBeanDefinition(beanName);
   }
}

protected void resetBeanDefinition(String beanName) {
   // S1 : 如果已經建立 ,需要清空 Merge BeanDefinition
   clearMergedBeanDefinition(beanName);

   // S2 : 銷毀 Bean
   destroySingleton(beanName);

   // S3 : 調用 PostProcessors 重置處理器進行處理
   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 中進行的

public void preInstantiateSingletons() throws BeansException {
 
   List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
   // 對所有的 BeanDefinition 進行循環處理
   for (String beanName : beanNames) {
      RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
      // 排除掉懶加載的Bean
      if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
         // 此處省略工廠類的判斷及處理 ...
         
         // 進入Bean擷取邏輯
         getBean(beanName);
      }
   }
    
    //.....
}
複制代碼           

總結

分析 BeanDefinition 不是這階段的主要目的 , 後續會有幾篇應用的文章來着重思考下如何進行業務定制

其實寫源碼文章是最輕松的 , 看懂就完事了 , 而寫定制或者業務 , 往往寫着寫着發現有地方沒搞懂 , 就需要回頭繼續看這個點 , 難度要大得多.......

作者:AntBlack

連結:https://juejin.cn/post/7157695463415087140