天天看點

詳實明了的IOC容器的介紹,啟動流程以及Bean的執行個體化和依賴注入

文章目錄

  • ​​前言​​
  • ​​項目環境​​
  • ​​核心要點​​
  • ​​IOC容器的啟動過程​​
  • ​​1. 資源定位,找到配置檔案​​
  • ​​2.BeanDefinition的載入和解析,将配置檔案解析成BeanDefiniton​​
  • ​​3. BeanDefinition的注冊,将BeanDefinition向Map中注冊`beanDefinitionMap`​​
  • ​​Bean的執行個體化和依賴注入​​
  • ​​Bean的執行個體化​​
  • ​​流程分析​​
  • ​​執行個體化Bean​​
  • ​​Bean的依賴注入(屬性注入)​​
  • ​​總結​​

前言

今天我們來認識一下Spring IOC容器,本文主要介紹SpringIOC容器的核心要點以及其啟動流程和執行個體化流程。

項目環境

Springframework 4.3.12

核心要點

Spring IOC是什麼?他有什麼作用呢?我們通過了解學習,Spring IOC是一個容器,用于生成和管理Bean的執行個體,以及執行個體之間的依賴關系,然後注入相關的依賴。這裡我們可以把IOC容器想象成一個餐館。我們去餐館點菜的話,不需要關心菜的生成過程,不需要關心菜的原材料從哪裡來。我們隻需要最終做好的菜。這裡的菜就是我們的需要的Bean。不同的菜對應不同的Bean。沒有IOC 容器的情況下,如果需要一個Bean的話,就需要自己來new一個對象的執行個體,比如A類依賴了B類,那麼就需要在A類中new一個B類的執行個體對象,這就好像我們要自己在家動手做菜一樣。有了IOC容器之後,如果A類依賴B類,隻需要通過IOC容器幫我們建立A類的執行個體和B類的執行個體,然後IOC容器會将B類的執行個體注入到A類中。這就很像餐館把菜做好之後送給我們一樣。既然IOC容器這麼6,那麼他是如何實作這一切的呢?

還是回到餐館那個例子,做菜的話就需要與原材料和菜單,同樣的IOC容器想要管理各個業務對象以及他們之間的依賴關系,就需要通過某種途徑來記錄和管理這些資訊,而BeanDefinition對象就承擔了這個責任。IOC容器中每一個Bean都會有一個對應的BeanDefinition執行個體,該執行個體負責儲存bean對象的所有必要資訊,包括Bean對象的class類型,是否是抽象類,構造方法和參數,以及其他屬性等,這裡的BeanDefinition就相當于原材料。而BeanDefinitionRegistry對象和BeanFactory對象就相當于菜單,告訴我們如何将原材料加工成相應的菜肴。

下面我們就來看看這些比較核心的類和接口。

詳實明了的IOC容器的介紹,啟動流程以及Bean的執行個體化和依賴注入
作用
BeanFactory BeanFactory接口主要包括了getBean,containsBean,getAllases等方法,作用是操作Bean
BeanDefinitionRegistry BeanDefinitionRegistry接口抽象出了Bean的注冊邏輯,其主要包括了registerBeanDefinition,removeBeanDefinition,getBeanDefinition等方法
ConfigurableListableBeanFactory ConfigurableListableBeanFactory接口包括了getBeanDefinition等方法,可以擷取BeanDefinition執行個體
DefaultListableBeanFactory DefaultListableBeanFactory類同時實作了ConfigurableListableBeanFactory接口和BeanDefinitionRegistry接口,說明其承擔了Bean的注冊和管理工作
BeanDefinition BeanDefinition接口是用來存儲Bean對象的必要資訊,包括Bean對象的class類型,是否是抽象類,構造方法和參數,依賴關系,以及其他屬性等
PropertyValue 這個類就是具體存放Bean對象的屬性資訊以及其依賴資訊

認識上面的幾個核心接口和類,對我們下面看Bean的啟動過程和執行個體化過程有很大的幫助。

需要說明的是,在Spring中,ApplicationContext是IOC容器的承載體,而BeanFactory是操作這個容器的工具,兩者關系緊密,互相協作,refresh方法實作了ApplicationContext和BeanFactory互相協作的過程,不同之處主要在于子類 AbstractRefreshableApplicationContext 和 GenericApplicationContext 中實作,兩者使用的 BeanFactory 都為 DefaultListableBeanFactory,它建構在BeanFactory之 上,屬于更⾼級的容器,除了具有BeanFactory的所有能⼒之外,還提供對事件監聽機制以及國際化的⽀持等。它管理的bean,在容器啟動 時全部完成初始化和依賴注⼊操作。

IOC容器的啟動過程

介紹完了IOC容器的核心類和要點,接下來我們看看IOC容器的啟動過程,其啟動過程主要有如下三個步驟:

1. 資源定位,找到配置檔案

這裡定位資源有兩種方式,一種是通過​

​ClassPathXmlApplicationContext​

​​類來解析Spring的配置檔案的形式,就是通過配置檔案來定義Bean的情況,另外,一種情況就是通過注解的方式來定義Bean的情況,這種情況是通過​

​AnnotationConfigApplicationContext​

​​類解析的,主要是掃描項目的classPath下定義的注解。下面我們首先介紹下通過​

​ClassPathXmlApplicationContext​

​​。這個類的核心作用是作為一個解析Xml的入口,其調用鍊是: ​

​ClassPathXmlApplicationContext​

​​類的構造器

------>​​

​AbstractApplicationContext​

​​類的​

​refresh​

​​方法

----->調用​​

​AbstractRefreshableApplicationContext​

​​類的​

​refreshBeanFactory​

​​方法

---->​​

​XmlWebApplicationContext​

​​類的​

​loadBeanDefinitions​

​​方法

----> ​​

​AbstractBeanDefinitionReader​

​​類的​

​loadBeanDefinitions​

​​方法

---->​​

​XmlBeanDefinitionReader​

​​類的​

​loadBeanDefinitions​

​​方法

---->​​

​XmlBeanDefinitionReader​

​​類的​

​doLoadBeanDefinitions​

​​方法

---->​​

​XmlBeanDefinitionReader​

​​類的​

​registerBeanDefinitions​

​​方法

---->​​

​DefaultBeanDefinitionDocumentReader​

​​類的​

​registerBeanDefinitions​

​​方法

調用層次很深,我們就直接跳到核心的方法來看。下面我們就來看看​​

​registerBeanDefinitions​

​方法

@Override
  public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    this.readerContext = readerContext;
    logger.debug("Loading bean definitions");
    //讀取XML檔案
    Element root = doc.getDocumentElement();
    //載入并注冊BeanDefinition
    doRegisterBeanDefinitions(root);
  }      

然後,​

​registerBeanDefinitions​

​​方法隻是讀取到根節點root之後,就另外一個核心方法​

​doRegisterBeanDefinitions​

​​方法,然後,​

​doRegisterBeanDefinitions​

​​方法又把邏輯轉給了​

​parseBeanDefinitions​

​​方法,這個​

​parseBeanDefinitions​

​​方法首先擷取所有的子節點 ,然後周遊解析子節點,載入​

​BeanDefinition​

​​又交給了​

​parseDefaultElement​

​​方法和​

​parseCustomElement​

​方法。

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    if (delegate.isDefaultNamespace(root)) {
      //擷取子節點
      NodeList nl = root.getChildNodes();
      for (int i = 0; i < nl.getLength(); i++) {
        Node node = nl.item(i);
        if (node instanceof Element) {
          Element ele = (Element) node;
          if (delegate.isDefaultNamespace(ele)) {
            //解析節點
            parseDefaultElement(ele, delegate);
          }
          else {
            delegate.parseCustomElement(ele);
          }
        }
      }
    }
    else {
      delegate.parseCustomElement(root);
    }
  }      

2.BeanDefinition的載入和解析,将配置檔案解析成BeanDefiniton

說完了配置檔案的解析之後,接下來,我們來看看​

​BeanDefinition​

​​的載入和解析。我們直接找到​

​parseDefaultElement​

​方法。

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    //省略部分非核心代碼
    //如果節點是bean節點,說明是一個Bean
    else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
      processBeanDefinition(ele, delegate);
    }
  }      

這個方法按照節點名,調用不同的處理方法,在此處我們隻看節點為bean時調用的方法​

​processBeanDefinition​

​方法。

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    if (bdHolder != null) {
      bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
      try {
        // Register the final decorated instance.(注冊BeanDefinition)
        BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
      }
      catch (BeanDefinitionStoreException ex) {
        getReaderContext().error("Failed to register bean definition with name '" +
            bdHolder.getBeanName() + "'", ele, ex);
      }
      // Send registration event.
      getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    }
  }      

我們重點看​

​BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());​

​​這個方法,這個方法才是真正的将傳入​

​BeanDefinitionRegistry​

​類,載入并解析BeanDefinition,然後對BeanDefinition進行注冊。

3. BeanDefinition的注冊,将BeanDefinition向Map中注冊​

​beanDefinitionMap​

接下來就到了我們的重頭戲,注冊BeanDefinition到​

​beanDefinitionMap​

​​中,其中key就是Bean的id,其中​

​beanDefinitionMap​

​​是一個定義在​

​DefaultListableBeanFactory​

​​類中全局的線程安全的map,用于存放解析到的​

​BeanDefinition​

​。

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256);      

讓我們來看看​

​registerBeanDefinition​

​方法吧,這個方法核心的步驟有兩步:

  1. 根據傳入的beanName檢查beanDefinition是否存在,如果存在就是一系列的校驗,主要是保證BeanDefinition的單例性,就是說IOC容器中每個Bean的執行個體時單例的。
  2. 将傳入的​

    ​beanDefinition​

    ​​執行個體放到​

    ​beanDefinitionMap​

    ​中。
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
      throws BeanDefinitionStoreException {
        if (hasBeanCreationStarted()) {
        // Cannot modify startup-time collection elements anymore (for stable iteration)
        //加鎖,保證線程安全
        synchronized (this.beanDefinitionMap) {
        // 将beanDefinition值設定到beanDefinitionMap中
          this.beanDefinitionMap.put(beanName, beanDefinition);
          List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
          updatedDefinitions.addAll(this.beanDefinitionNames);
          updatedDefinitions.add(beanName);
          this.beanDefinitionNames = updatedDefinitions;
          if (this.manualSingletonNames.contains(beanName)) {
            Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
            updatedSingletons.remove(beanName);
            this.manualSingletonNames = updatedSingletons;
          }
        }
      }
      else {
        // Still in startup registration phase,将beanDefinition值設定到beanDefinitionMap中
        this.beanDefinitionMap.put(beanName, beanDefinition);
        this.beanDefinitionNames.add(beanName);
        this.manualSingletonNames.remove(beanName);
      }
      this.frozenBeanDefinitionNames = null;
      }      

小結,至此,我們對IOC容器的初始化過程就解析完了,其實其初始化過程還是比較簡單的,隻是Spring的代碼結構比較深,核心代碼不好找。

Bean的執行個體化和依賴注入

說完了IOC容器的初始化過程,接下來,我們來看看IOC容器的執行個體化過程。經過上一個階段,所有Bean定義都通過​

​BeanDefinition​

​​的方式注冊到了​

​BeanDefinitionRegistry​

​​中。當某個請求通過容器的​

​getBean​

​​方法請求某個對象,或者因為依賴關系容器需要隐式的調用​

​getBean​

​​方法時,就會觸發第二階段的活動,IOC容器首先檢查所請求的對象是否已經執行個體化完成,如果沒有,則會根據注冊的​

​BeanDefinition​

​所提供的資訊執行個體化請求對象。并為其注入依賴,當該對象裝配完成後,容器會立即傳回給請求方法。

Bean的執行個體化

讓我們從前面提到的​

​getBean​

​​方法說起,這裡的調用鍊如下:

​​

​AbstractBeanFactory​

​​類的​

​getBean​

​​方法

----->​​

​AbstractBeanFactory​

​​類的​

​doGetBean​

​​方法

----->​​

​AbstractBeanFactory​

​​類的​

​createBean​

​​方法

------>​​

​AbstractAutowireCapableBeanFactory​

​​類的​

​doCreateBean​

​​方法(這個方法主要是)

建立Bean的核心邏輯就在​​

​AbstractAutowireCapableBeanFactory​

​​類的​

​doCreateBean​

​方法中:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
    // Instantiate the bean(執行個體化Bean)
        BeanWrapper instanceWrapper = null;
        if (mbd.isSingleton()) {
          //如果是單例Bean,首先清理FactoryBean緩存
            instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
        }
        if (instanceWrapper == null) {
            //使用特定的政策執行個體化Bean,如果工廠方法、構造器等,将BeanDefinition轉換為BeanWrapper
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
            final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
        Class beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
        // Allow post-processors to modify the merged bean definition. 允許MergedBeanDefinitionPostProcessor修改BeanDefinition
         synchronized (mbd.postProcessingLock) {
        if (!mbd.postProcessed) {
            try {
                applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                                "Post-processing of merged bean definition failed", ex);
            }
            mbd.postProcessed = true;
        }
    }
    // Eagerly cache singletons to be able to resolve circular references  
        // even when triggered by lifecycle interfaces like BeanFactoryAware.
        //需要提前暴露Bean的條件:單例&&允許循環依賴&&目前Bean正在建立中,檢測到了循環依賴
         boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                                      isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
        if (logger.isTraceEnabled()) {
            logger.trace("Eagerly caching bean '" + beanName +
                         "' to allow for resolving potential circular references");
        }
        //對于需要提前暴露的Bean,以其ObjectFactory的形式放入singletonFactories中,以解決循環依賴的問題
        //ObjectFactory所建立的Bean由getEarlyBeanReference()方法指定
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }
    // Initialize the bean instance. Bean的初始化
     Object exposedObject = bean;
    try {
        //對Bean進行屬性的填充。此外,如果依賴了其他Bean,則會在這裡注入依賴
        populateBean(beanName, mbd, instanceWrapper);
        //執行Bean的初始化方法,如配置的init-method等
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    }
    //循環依賴檢查
    if (earlySingletonExposure) {
        Object earlySingletonReference = getSingleton(beanName, false);
        if (earlySingletonReference != null) {
            if (exposedObject == bean) {
                exposedObject = earlySingletonReference;
            }
            else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                String[] dependentBeans = getDependentBeans(beanName);
                Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
                for (String dependentBean : dependentBeans) {
                    if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                        actualDependentBeans.add(dependentBean);
                    }
                }
                if (!actualDependentBeans.isEmpty()) {
                        //省略部分代碼
                        throw new BeanCurrentlyInCreationException();
                }
            }
        }
    }

    try {
        //注冊Bean的銷毀方法,如destroy-method
        registerDisposableBeanIfNecessary(beanName, bean, mbd);
    }
    catch (BeanDefinitionValidationException ex) {
        throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
    }
    return exposedObject;
}

        
}      

從doCreateBean方法我們可以看出Bean生命周期的一個完整過程,這個方法裡面的流程很長,知識點很多。下面我們就對流程進行具體的分析。

流程分析

執行個體化Bean

執行個體化Bean的邏輯是在​

​SimpleInstantiationStrategy​

​​類的​

​instantiate​

​方法中實作的。Bean的執行個體化其實就是将指定的BeanDefinition轉換成BeanWrapper,然後通過指定構造器和預設無參構造器,CGLB動态代理等方式來執行個體化Bean,這時執行個體化後的Bean是一個剛執行個體化好的,屬性未指派的空Bean。

@Override
  public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
    //省略部分代碼
    // Don't override the class with CGLIB if no overrides.
    if (bd.getMethodOverrides().isEmpty()) {
      //通過構造器執行個體化Bean  
      return BeanUtils.instantiateClass(constructorToUse);
    }
    else {
      // Must generate CGLIB subclass.(通過CGLIB的方式生成Bean)
      return instantiateWithMethodInjection(bd, beanName, owner);
    }
  }      

Bean的依賴注入(屬性注入)

說完了Bean的執行個體化,接下來我們來說下Bean的依賴注入。屬性注入必須用到PropertyValue類,這個類儲存了Bean的所有屬性和依賴資訊。

依賴注入的調用流程是​​

​AbstractAutowireCapableBeanFactory​

​​類的​

​applyPropertyValues​

​方法。

protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
      //類型轉換接口
      TypeConverter converter = getCustomTypeConverter();
    if (converter == null) {
      converter = bw;
    }
    BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);

  String propertyName = pv.getName();
                    //對于ref來說就是beanName,對于value 來說就是value
        Object originalValue = pv.getValue();
        Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
        boolean convertible = bw.isWritableProperty(propertyName) &&
            !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
        if (convertible) {
          convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
        }
  try {
      //設定依賴屬性
      bw.setPropertyValues(new MutablePropertyValues(deepCopy));
    }
    catch (BeansException ex) {
      throw new BeanCreationException(
          mbd.getResourceDescription(), beanName, "Error setting property values", ex);
    }

  }
  
    private Object convertForProperty(Object value, String propertyName, BeanWrapper bw, TypeConverter converter) {
    if (converter instanceof BeanWrapperImpl) {
      return ((BeanWrapperImpl) converter).convertForProperty(value, propertyName);
    }
    else {
      PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
      MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
      return converter.convertIfNecessary(value, pd.getPropertyType(), methodParam);
    }
  }      

總結