1. 程式人生 > >Spring IOC原始碼詳解之容器初始化

Spring IOC原始碼詳解之容器初始化

可以進入parseBeanDefinitionElement方法中,裡面詳細講了Bean元素是如何解析的。
//解析<Bean>元素的入口  
   public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {  
       return parseBeanDefinitionElement(ele, null);  
   }  

   //解析Bean定義資原始檔中的<Bean>元素,這個 方 法中主要處理<Bean>元素的id,name  
   //和別名屬性  
   public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {  
       //獲取<Bean>元素中的id屬性值  
       String id = ele.getAttribute(ID_ATTRIBUTE);  
       //獲取<Bean>元素中的name屬性值  
       String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);  
       ////獲取<Bean>元素中的alias屬性值  
       List<String> aliases = new ArrayList<String>();  
       //將<Bean>元素中的所有name屬性值存放到別名中  
       if (StringUtils.hasLength(nameAttr)) {  
           String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS);  
           aliases.addAll(Arrays.asList(nameArr));  
       }  
       String beanName = id;  
       //如果<Bean>元素中沒有配置id屬性時,將別名中的第一個值賦值給beanName  
       if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {  
           beanName = aliases.remove(0
); if (logger.isDebugEnabled()) { logger.debug("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases"); } } //檢查<Bean>元素所配置的id或者name的唯一性,containingBean標識<Bean> //元素中是否包含子<Bean>元素 if (containingBean == null) { //檢查<Bean>元素所配置的id、name或者別名是否重複 checkNameUniqueness(beanName, aliases, ele); } //詳細對<Bean>元素中配置的Bean定義進行解析的地方 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { if (!StringUtils.hasText(beanName)) { try { if (containingBean != null) { //如果<Bean>元素中沒有配置id、別名或者name,且沒有包含子//<Bean>元素,為解析的Bean生成一個唯一beanName並註冊 beanName = BeanDefinitionReaderUtils.generateBeanName( beanDefinition, this.readerContext.getRegistry(), true); } else { //如果<Bean>元素中沒有配置id、別名或者name,且包含了子//<Bean>元素,為解析的Bean使用別名向IoC容器註冊 beanName = this.readerContext.generateBeanName(beanDefinition); //為解析的Bean使用別名註冊時,為了向後相容 //Spring1.2/2.0,給別名新增類名字尾 String beanClassName = beanDefinition.getBeanClassName(); if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { aliases.add(beanClassName); } } if (logger.isDebugEnabled()) { logger.debug("Neither XML 'id' nor 'name' specified - " + "using generated bean name [" + beanName + "]"); } } catch (Exception ex) { error(ex.getMessage(), ele); return null; } } String[] aliasesArray = StringUtils.toStringArray(aliases); return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } //當解析出錯時,返回null return null; } //詳細對<Bean>元素中配置的Bean定義其他屬性進行解析,由於上面的方法中已經對//Bean的id、name和別名等屬性進行了處理,該方法中主要處理除這三個以外的其他屬性資料 public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, BeanDefinition containingBean) { //記錄解析的<Bean> this.parseState.push(new BeanEntry(beanName)); //這裡只讀取<Bean>元素中配置的class名字,然後載入到BeanDefinition中去 //只是記錄配置的class名字,不做例項化,物件的例項化在依賴注入時完成 String className = null; if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } try { String parent = null; //如果<Bean>元素中配置了parent屬性,則獲取parent屬性的值 if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } //根據<Bean>元素配置的class名稱和parent屬性值建立BeanDefinition //為載入Bean定義資訊做準備 AbstractBeanDefinition bd = createBeanDefinition(className, parent); //對當前的<Bean>元素中配置的一些屬性進行解析和設定,如配置的單態(singleton)屬性等 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); //為<Bean>元素解析的Bean設定description資訊 bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); //對<Bean>元素的meta(元資訊)屬性解析 parseMetaElements(ele, bd); //對<Bean>元素的lookup-method屬性解析 parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); //對<Bean>元素的replaced-method屬性解析 parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); //解析<Bean>元素的構造方法設定 parseConstructorArgElements(ele, bd); //解析<Bean>元素的<property>設定 parsePropertyElements(ele, bd); //解析<Bean>元素的qualifier屬性 parseQualifierElements(ele, bd); //為當前解析的Bean設定所需的資源和依賴物件 bd.setResource(this.readerContext.getResource()); bd.setSource(extractSource(ele)); return bd; } catch (ClassNotFoundException ex) { error("Bean class [" + className + "] not found", ele, ex); } catch (NoClassDefFoundError err) { error("Class that bean class [" + className + "] depends on not found", ele, err); } catch (Throwable ex) { error("Unexpected failure during bean definition parsing", ele, ex); } finally { this.parseState.pop(); } //解析<Bean>元素出錯時,返回null return null; }
上面方法中一些對一些配置如元資訊(meta)、qualifier等的解析,我們在Spring中配置時使用的也不多,我們在使用Spring的元素時,配置最多的是property屬性
    //解析<property>元素中ref,value或者集合等子元素  
   public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) {  
       //如果<property>沒有使用Spring預設的名稱空間,則使用使用者自定義的規則解析//內嵌元素  
       if (!isDefaultNamespace(ele)) {  
           return parseNestedCustomElement(ele, bd);  
       }  
       //如果子元素是bean,則使用解析<Bean>元素的方法解析  
       else if (nodeNameEquals(ele, BEAN_ELEMENT)) {  
           BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);  
           if (nestedBd != null) {  
               nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);  
           }  
           return nestedBd;  
       }  
       //如果子元素是ref,ref中只能有以下3
個屬性:bean、local、parent else if (nodeNameEquals(ele, REF_ELEMENT)) { //獲取<property>元素中的bean屬性值,引用其他解析的Bean的名稱 //可以不再同一個Spring配置檔案中,具體請參考Spring對ref的配置規則 String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE); boolean toParent = false; if (!StringUtils.hasLength(refName)) { //獲取<property>元素中的local屬性值,引用同一個Xml檔案中配置 //的Bean的id,local和ref不同,local只能引用同一個配置檔案中的Bean refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE); if (!StringUtils.hasLength(refName)) { //獲取<property>元素中parent屬性值,引用父級容器中的Bean refName = ele.getAttribute(PARENT_REF_ATTRIBUTE); toParent = true; if (!StringUtils.hasLength(refName)) { error("'bean', 'local' or 'parent' is required for <ref> element", ele); return null; } } } //沒有配置ref的目標屬性值 if (!StringUtils.hasText(refName)) { error("<ref> element contains empty target attribute", ele); return null; } //建立ref型別資料,指向被引用的物件 RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent); //設定引用型別值是被當前子元素所引用 ref.setSource(extractSource(ele)); return ref; } //如果子元素是<idref>,使用解析ref元素的方法解析 else if (nodeNameEquals(ele, IDREF_ELEMENT)) { return parseIdRefElement(ele); } //如果子元素是<value>,使用解析value元素的方法解析 else if (nodeNameEquals(ele, VALUE_ELEMENT)) { return parseValueElement(ele, defaultValueType); } //如果子元素是null,為<property>設定一個封裝null值的字串資料 else if (nodeNameEquals(ele, NULL_ELEMENT)) { TypedStringValue nullHolder = new TypedStringValue(null); nullHolder.setSource(extractSource(ele)); return nullHolder; } //如果子元素是<array>,使用解析array集合子元素的方法解析 else if (nodeNameEquals(ele, ARRAY_ELEMENT)) { return parseArrayElement(ele, bd); } //如果子元素是<list>,使用解析list集合子元素的方法解析 else if (nodeNameEquals(ele, LIST_ELEMENT)) { return parseListElement(ele, bd); } //如果子元素是<set>,使用解析set集合子元素的方法解析 else if (nodeNameEquals(ele, SET_ELEMENT)) { return parseSetElement(ele, bd); } //如果子元素是<map>,使用解析map集合子元素的方法解析 else if (nodeNameEquals(ele, MAP_ELEMENT)) { return parseMapElement(ele, bd); } //如果子元素是<props>,使用解析props集合子元素的方法解析 else if (nodeNameEquals(ele, PROPS_ELEMENT)) { return parsePropsElement(ele); } //既不是ref,又不是value,也不是集合,則子元素配置錯誤,返回null else { error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele); return null; } }
上面程式碼中對property元素中配置的Array、List、Set、Map、Prop等各種集合子元素的都通過上述方法解析,生成對應的資料物件,比如ManagedList、ManagedArray、ManagedSet等,這些Managed類是Spring物件BeanDefiniton的資料封裝,對集合資料型別的具體解析有各自的解析方法實現。如果想了解其中的一種集合是如何解析的 ,進入相應的方法。

5、Bean是如何在IOC容器中註冊的

下面看解析完的bean是怎樣在IOC容器中註冊的:接著4中的程式碼,進入
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
    public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {

        //得到需要 注冊 的 bean名字
        String beanName = definitionHolder.getBeanName();
        //開始註冊
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

        // 別名也是可以 通過IOC容器和bean聯絡起來的進行註冊 
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String alias : aliases) {
                registry.registerAlias(beanName, alias);
            }
        }
    }
上面的registry物件是BeanDefinitionRegistry,上一步是BeanDefinitionReaderUtils.registerBeanDefinition(),即當呼叫BeanDefinitionReaderUtils向IoC容器註冊解析的BeanDefinition時,真正完成註冊功能的是DefaultListableBeanFactory。那麼DefaultListableBeanFactory怎麼會和BeanDefinitionReaderUtils扯到一起呢?

其實上面的類圖還忽略了一點,就是

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
        implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {}


DefaultListableBeanFactory 實現了BeanDefinitionRegistry介面,這下就豁然開朗,那麼DefaultListableBeanFactory是如何註冊的呢

DefaultListableBeanFactory中使用一個HashMap的集合物件存放IoC容器中註冊解析的BeanDefinition,
@Override
    //---------------------------------------------------------------------
    // 這裡是IOC容器對BeanDefinitionRegistry介面的實現
    //---------------------------------------------------------------------

    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {

        .....//這裡省略了對BeanDefinition的驗證過程
        //先看看在容器裡是不是已經有了同名的bean,如果有丟擲異常。
        Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
        if (oldBeanDefinition != null) {
            if (!this.allowBeanDefinitionOverriding) {
            ...........
        }
        else {
            //把bean的名字加到IOC容器中去
            this.beanDefinitionNames.add(beanName);
        }
        //這裡把bean的名字和Bean定義聯絡起來放到一個HashMap中去,IOC容器通過這個Map來維護容器裡的Bean定義資訊。
        this.beanDefinitionMap.put(beanName, beanDefinition);
        removeSingleton(beanName);
    }


這樣就完成了Bean定義在IOC容器中的註冊,就可被IOC容器進行管理和使用了

總結IOC初始化過程

  • 1、setConfigLocations方法

  • 2、初始化的入口在容器實現中的 refresh()呼叫來完成

  • 3、AbstractRefreshableApplicationContext實現的 refreshBeanFactory()方法

  • 4、建立DefaultListableBeanFactory,並呼叫loadBeanDefinitions(beanFactory)裝載bean定義

  • 5、轉到XmlBeanDefinitionReader中的loadBeanDefinitions。

  • 6、XmlBeanDefinitionReader通過呼叫其父類DefaultResourceLoader的getResource方法獲取要載入的資源

  • 7 、DocumentLoader將Bean定義資源轉換成Document物件

  • 8、doLoadBeanDefinitions中進入registerBeanDefinitions 解 析 D ocument物件

  • 9、DefaultListableBeanFactory中使用一個HashMap的集合物件存放IoC容器中註冊解析的BeanDefinition,