Spring4.3.x 淺析xml配置的解析過程(4)——解析bean標籤及其所有子標籤

版權聲明:本文爲博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/chyohn/article/details/54945490

概述


使用ResourceLoader創建Resource對象一節中,我們探討了Spring如何正確的查找我們指定的配置文件併爲配置文件生成Resource對象。

使用DocumentLoader創建Document對象一節中,我們又已經解析了Spring通過xerces如何把Resource對象中的XML內容轉換成Document對象。

淺析Spring4使用XmlBeanDefinitionReader解析xml配置一文中,我們知道XmlBeanDefinitionReader使用BeanDefinitionDocumentReader接口的默認實現DefaultBeanDefinitionDocumentReader把Document對象中包含的配置信息轉換成BeanDefinition對象並把它註冊到BeanDefintionRegistry對象中。在DefaultBeanDefinitionDocumentReader的實現中,它的責任是遍歷xml根節點下的子節點,並把處理bean標籤和自定義命名空間的標籤(比如aop:,context:,p:等)的細節委託給BeanDefinitionParserDelegate對象,BeanDefinitionParserDelegate纔是真正解析配置文件的地方。

下面我們就開始探討BeanDefinitionParserDelegate解析bean標籤的過程。

約定:爲了提高本文的可讀性,我把spring源碼中定義的常量替換爲對應的字面值。

(1)創建BeanDefinitionParserDelegate 對象
在DefaultBeanDefinitionDocumentReader中,調用createDelegate方法根據beans標籤來創建和初始化BeanDefinitionParserDelegate 對象,見下面createDelegate方法的源碼。

    protected BeanDefinitionParserDelegate createDelegate(
            XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) {

        // 創建delegate對象
        BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
        // 初始化delegate對象
        delegate.initDefaults(root, parentDelegate);
        return delegate;
    }

createDelegate方法中的root參數爲xml配置中<beans>標籤,它首先實例化一個BeanDefinitionParserDelegate對象,然後調用這個對象的initDefaults方法來初始化默認值,這個方法的代碼如下。

    public void initDefaults(Element root, BeanDefinitionParserDelegate parent) {
        // 計算默認值
        populateDefaults(this.defaults, (parent != null ? parent.defaults : null), root);
        this.readerContext.fireDefaultsRegistered(this.defaults);
    }
    protected void populateDefaults(DocumentDefaultsDefinition defaults, DocumentDefaultsDefinition parentDefaults, Element root) {
        String lazyInit = root.getAttribute("default-lazy-init");
        if ("default".equals(lazyInit)) {
            // 繼承外層<beans>標籤的屬性值,否則爲false字符串
            lazyInit = (parentDefaults != null ? parentDefaults.getLazyInit() : "false");
        }
        defaults.setLazyInit(lazyInit);

        String merge = root.getAttribute("default-merge");
        if ("default".equals(merge)) {
            // 繼承外層<beans>標籤的屬性值,否則爲false字符串
            merge = (parentDefaults != null ? parentDefaults.getMerge() : "false");
        }
        defaults.setMerge(merge);

        String autowire = root.getAttribute("default-autowire");
        if ("default".equals(autowire)) {
            // 繼承外層<beans>標籤的屬性值,否則爲no字符串
            autowire = (parentDefaults != null ? parentDefaults.getAutowire() : "no");
        }
        defaults.setAutowire(autowire);

        defaults.setDependencyCheck(root.getAttribute("default-dependency-check"));

        if (root.hasAttribute("default-autowire-candidates")) {
            defaults.setAutowireCandidates(root.getAttribute("default-autowire-candidates"));
        } else if (parentDefaults != null) {
            // 繼承外層<beans>標籤的屬性值
            defaults.setAutowireCandidates(parentDefaults.getAutowireCandidates());
        }

        if (root.hasAttribute("default-init-method")) {
            defaults.setInitMethod(root.getAttribute("default-init-method"));
        } else if (parentDefaults != null) {
            // 繼承外層<beans>標籤的屬性值
            defaults.setInitMethod(parentDefaults.getInitMethod());
        }

        if (root.hasAttribute("default-destroy-method")) {
            defaults.setDestroyMethod(root.getAttribute("default-destroy-method"));
        } else if (parentDefaults != null) {
            // 繼承外層<beans>標籤的屬性值
            defaults.setDestroyMethod(parentDefaults.getDestroyMethod());
        }

        defaults.setSource(this.readerContext.extractSource(root));
    }

populateDefaults方法獲取<beans>標籤的屬性值,如果beans標籤上沒有指定屬性值並且外層還有beans標籤時,將沿用外層beans標籤的。

(2)使用BeanDefinitionParserDelegate 對象處理<bean>標籤
我們從DefaultBeanDefinitionDocumentReader使用BeanDefinitionParserDelegate解析<bean>標籤的方法processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)開始,代碼如下。

    /**
    * 解析bean節點,並註冊BeanDefinition對象
    */
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {

        // 創建BeanDefinitionHolder
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            // 裝飾BeanDefinition
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // 註冊已經創建好的BeanDefintion
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            } catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" +
                        bdHolder.getBeanName() + "'", ele, ex);
            }
            // 發送BeanDefinition註冊事件
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }

processBeanDefinition方法的ele參數爲<bean>標籤,這段代碼分成三步。第一步,根據傳入的Element對象(bean標籤的)調用代理對象的parseBeanDefinitionElement(Element ele)方法創建BeanDefinitionHolder 對象,這個對象持有創建好的BeanDefinition對象、bean的id和bean的別名。

第二步,調用代理對象的decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder)來對BeanDefinition對象再加工,主要是解析<bean>標籤中自定義屬性和自定義標籤。

第三步,調用工具類BeanDefinitionReaderUtils的registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)方法,這個方法用於註冊創建好的BeanDefinition。

第三步已經在淺析Spring4使用XmlBeanDefinitionReader解析xml配置部分探討了。這裏我們深入的探討前兩步。

1. 創建BeanDefinitionHolder 對象


執行BeanDefinitionParserDelegate的parseBeanDefinitionElement(Element ele)方法,代碼如下。

    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
        return parseBeanDefinitionElement(ele, null);
    }
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
        // 獲取id屬性
        String id = ele.getAttribute("id");
        // 獲取name屬性
        String nameAttr = ele.getAttribute("name");

        List<String> aliases = new ArrayList<String>();
        if (StringUtils.hasLength(nameAttr)) {
            String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, ",; ");
            aliases.addAll(Arrays.asList(nameArr));
        }

        String beanName = id;
        if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
            // 使用第一個alias作爲id
            beanName = aliases.remove(0);
            if (logger.isDebugEnabled()) {
                logger.debug("No XML 'id' specified - using '" + beanName +
                        "' as bean name and " + aliases + " as aliases");
            }
        }

        if (containingBean == null) {
            // 檢查id和別名是否已經被使用了,如果已經被其他bean佔用,則會拋出異常
            checkNameUniqueness(beanName, aliases, ele);
        }

        // 創建BeanDefinition對象
        AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
        if (beanDefinition != null) {
            if (!StringUtils.hasText(beanName)) {
                // 配置中沒有指定id屬性
                try {
                    if (containingBean != null) {
                        // bean是另一個bean的內部bean

                        beanName = BeanDefinitionReaderUtils.generateBeanName(
                                beanDefinition, this.readerContext.getRegistry(), true);
                    }
                    else {
                        // 爲一個頂層bean生成id
                        beanName = this.readerContext.generateBeanName(beanDefinition);

                        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);
            // 返回BeanDefinitionHolder對象
            // 此對象持有生成的BeanDefinition對象和id以及別名列表
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        }

        return null;
    }

這部分代碼主要是檢查用戶給的bean id是否已經被佔用、爲沒有id屬性值的bean創建id值以及調用parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean)方法來解析Element對象並創建BeanDefinition對象,最後創建一個BeanDefinitionHolder對象來封裝BeanDefinition對象、bean id和bean別名。

parseBeanDefinitionElement方法是解析<bean>節點的主要方法,在這裏我們重點探討它。下面是BeanDefinitionParserDelegate的parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean)方法代碼。

/**
* 創建BeanDefinition對象
**/
public AbstractBeanDefinition parseBeanDefinitionElement(
            Element ele, String beanName, BeanDefinition containingBean) {

        this.parseState.push(new BeanEntry(beanName));

        String className = null;
        // 讀取<bean>節點的class屬性
        if (ele.hasAttribute("class")) {
            className = ele.getAttribute("class").trim();
        }

        try {
            String parent = null;
            // 讀取<bean>節點的parent屬性
            if (ele.hasAttribute("parent")) {
                parent = ele.getAttribute("parent");
            }

            // 創建一個GenericBeanDefinition對象
            AbstractBeanDefinition bd = createBeanDefinition(className, parent);

            // 解析<bean>節點的屬性
            parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
            // 獲取<desription>節點的值
            bd.setDescription(DomUtils.getChildElementValueByTagName(ele, "description"));

            // 解析<meta>節點
            parseMetaElements(ele, bd);
            // 解析<lookup-method>節點
            parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
            // 解析<replaced-method>節點
            parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

            // 解析<constructor-arg>節點
            parseConstructorArgElements(ele, bd);
            // 解析<property>節點
            parsePropertyElements(ele, bd);
            // 解析<qualifier>節點
            parseQualifierElements(ele, bd);

            // 讓BeanDefinition持有當前Resource對象
            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();
        }

        return null;
    }

這段代碼就是根據bean標籤的內容來實例化並初始化BeanDefinition對象。
parseBeanDefinitionElement方法制定了<bean>的解析流程,即BeanDefinition對象的創建和初始化流程,這個流程在parseBeanDefinitionElement方法中已經非常明確,主要有8步(除去<description>標籤),這裏我就不畫流程圖了,下面我們直接來探討這個流程。

1.1 實例化BeanDefinition對象

parseBeanDefinitionElement方法調用BeanDefinitionParserDelegate的createBeanDefinition方法來創建BeanDefinition對象,這個方法的源碼如下。

    protected AbstractBeanDefinition createBeanDefinition(String className, String parentName)
            throws ClassNotFoundException {

        return BeanDefinitionReaderUtils.createBeanDefinition(
                parentName, className, this.readerContext.getBeanClassLoader());
    }

createBeanDefinition方法通過調用工具類BeanDefinitionReaderUtils的createBeanDefinition靜態方法來創建BeanDefinition對象,代碼如下。

    public static AbstractBeanDefinition createBeanDefinition(
            String parentName, String className, ClassLoader classLoader) throws ClassNotFoundException {

        GenericBeanDefinition bd = new GenericBeanDefinition();
        bd.setParentName(parentName);
        if (className != null) {
            if (classLoader != null) {
                // 使用ClassLoader加載Class對象
                bd.setBeanClass(ClassUtils.forName(className, classLoader));
            } else {
                // 設置bean對於的class全名稱
                bd.setBeanClassName(className);
            }
        }
        return bd;
    }

BeanDefinitionReaderUtils的createBeanDefinition方法創建一個GenericBeanDefinition對象,設置此對象的parent名稱,以及對應的Class對象或者class全名稱。

1.2 解析<bean>標籤屬性

在parseBeanDefinitionElement方法中調用BeanDefinitionParserDelegate的parseBeanDefinitionAttributes方法處理<bean>標籤的屬性,parseBeanDefinitionAttributes方法的代碼如下。

    public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
            BeanDefinition containingBean, AbstractBeanDefinition bd) {

        // spring從4.0開始不再使用singleton屬性
        if (ele.hasAttribute("singleton")) {
            error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
        } else if (ele.hasAttribute("scope")) {
            // 設置作用域
            bd.setScope(ele.getAttribute("scope"));
        } else if (containingBean != null) {
            // 嵌套bean,則使用外面那個bean的作用域
            bd.setScope(containingBean.getScope());
        }

        if (ele.hasAttribute("abstract")) {
            bd.setAbstract("true".equals(ele.getAttribute("abstract")));
        }

        String lazyInit = ele.getAttribute("lazy-init");
        if ("default".equals(lazyInit)) {
            // 默認爲false字符串
            lazyInit = this.defaults.getLazyInit();
        }
        bd.setLazyInit("true".equals(lazyInit));

        String autowire = ele.getAttribute("autowire");
        bd.setAutowireMode(getAutowireMode(autowire));

        String dependencyCheck = ele.getAttribute("dependency-check");
        bd.setDependencyCheck(getDependencyCheck(dependencyCheck));

        if (ele.hasAttribute("depends-on")) {
            String dependsOn = ele.getAttribute("depends-on");
            bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, ",; "));
        }

        String autowireCandidate = ele.getAttribute("autowire-candidate");
        if ("".equals(autowireCandidate) || "default".equals(autowireCandidate)) {
            String candidatePattern = this.defaults.getAutowireCandidates();
            if (candidatePattern != null) {
                String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
                bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
            }
        } else {
            bd.setAutowireCandidate("true".equals(autowireCandidate));
        }

        if (ele.hasAttribute("primary")) {
            bd.setPrimary("true".equals(ele.getAttribute("primary")));
        }

        if (ele.hasAttribute("init-method")) {
            String initMethodName = ele.getAttribute("init-method");
            if (!"".equals(initMethodName)) {
                bd.setInitMethodName(initMethodName);
            }
        } else {
            if (this.defaults.getInitMethod() != null) {
                bd.setInitMethodName(this.defaults.getInitMethod());
                bd.setEnforceInitMethod(false);
            }
        }

        if (ele.hasAttribute("destroy-method")) {
            String destroyMethodName = ele.getAttribute("destroy-method");
            bd.setDestroyMethodName(destroyMethodName);
        } else {
            if (this.defaults.getDestroyMethod() != null) {
                bd.setDestroyMethodName(this.defaults.getDestroyMethod());
                bd.setEnforceDestroyMethod(false);
            }
        }

        if (ele.hasAttribute("factory-method")) {
            bd.setFactoryMethodName(ele.getAttribute("factory-method"));
        }
        if (ele.hasAttribute("factory-bean")) {
            bd.setFactoryBeanName(ele.getAttribute("factory-bean"));
        }

        return bd;
    }
    public int getAutowireMode(String attValue) {
         String att = attValue;
        if("default".equals(attValue)) {
            // 根據<beans>標籤的default-autowire屬性值確定
            att = this.defaults.getAutowire();
        }

        // 不啓用自動裝配
        int autowire = 0;
        if("byName".equals(att)) {
            // 查找與屬性名稱相同的bean,並注入
            autowire = 1;
        } else if("byType".equals(att)) {
            // 通過屬性的類型查找JavaBean依賴的對象併爲其注入
            autowire = 2;
        } else if("constructor".equals(att)) {
            // 同byType一樣是通過類型查找依賴對象
            // 與byType的區別:它不是使用Seter方法注入,而是使用構造子注入
            autowire = 3;
        } else if("autodetect".equals(att)) {
            // 在byType和constructor之間自動的選擇注入方式
            autowire = 4;
        }

        return autowire;
    }

    public int getDependencyCheck(String attValue) {
        String att = attValue;
        if ("default".equals(att)) {
            // 根據<beans>標籤的default-dependency-check屬性值確定
            att = this.defaults.getDependencyCheck();
        }
        if ("all".equals(att)) {
            // 檢查所有屬性
            return 3;
        } else if ("objects".equals(att)) {
            // 檢查對象的關聯關係
            return 1;
        } else if ("simple".equals(att)) {
            // 檢查原始類型和String類型的屬性
            return 2;
        } else {
            // 不檢查依賴
            return 0;
        }
    }

parseBeanDefinitionAttributes方法主要是解析bean標籤上的屬性,如果bean標籤上有些屬性沒有設置,則將使用在<beans>標籤上對應的屬性值。

1.3 解析<meta>標籤

在parseBeanDefinitionElement方法中調用BeanDefinitionParserDelegate的parseMetaElements方法處理<bean>標籤的子標籤<meta>,parseMetaElements方法的源碼如下。

    public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
        NodeList nl = ele.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (isCandidateElement(node) && nodeNameEquals(node, "meta")) {
                Element metaElement = (Element) node;
                String key = metaElement.getAttribute("key");
                String value = metaElement.getAttribute("value");
                BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
                attribute.setSource(extractSource(metaElement));
                attributeAccessor.addMetadataAttribute(attribute);
            }
        }
    }

parseMetaElements方法掃描bean標籤下的meta標籤,並獲取meta標籤上的key和value屬性值。這裏要說明兩個方法,其一是isCandidateElement方法,它是判斷當前節點是否需要要被處理的節點,代碼如下。

    /**
    * 判斷節點是否是待處理節點
    **/
    private boolean isCandidateElement(Node node) {
        return (node instanceof Element && (isDefaultNamespace(node) || !isDefaultNamespace(node.getParentNode())));
    }

具有以下條件中一條的節點是待處理節點。
a. 節點是Element對象,並且在默認命名空間中。
b. 節點是Element對象,且它和它的父節點都不在默認命名空間中。

其二是nodeNameEquals方法,它判斷節點的名稱是否爲指定的字符串,代碼如下。

    /**
    * 判斷節點名稱
    **/
    public boolean nodeNameEquals(Node node, String desiredName) {
        return desiredName.equals(node.getNodeName()) || desiredName.equals(getLocalName(node));
    }

isCandidateElement和nodeNameEquals方法我們還會在下面的探討中見到,所以這裏對他們都認識一下。

1.4 解析<lookup-method>標籤

<lookup-method>標籤用於一個無狀態bean引用一個有狀態bean,也可以這樣說一個作用域廣的bean引用作用域小的bean,比如singleton bean應用一個prototype bean、sesstion bean、request bean時。

在parseBeanDefinitionElement方法中調用BeanDefinitionParserDelegate的parseLookupOverrideSubElements方法處理<bean>標籤的子標籤<lookup-method>,parseLookupOverrideSubElements方法的源碼如下。

    public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
        NodeList nl = beanEle.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (isCandidateElement(node) && nodeNameEquals(node, "lookup-method")) {
                Element ele = (Element) node;
                // 方法名稱
                String methodName = ele.getAttribute("name");
                // 一個bean名稱,表示方法需要返回這個名稱的bean
                String beanRef = ele.getAttribute("bean");
                LookupOverride override = new LookupOverride(methodName, beanRef);
                override.setSource(extractSource(ele));
                overrides.addOverride(override);
            }
        }
    }

1.5 解析<replaced-method>標籤

<replaced-method>用於修改非final bean中某個非private且非final方法的實現。一般的,如果一個方法不是隻有包範圍內訪問的的,那麼可以直接通過繼承來重寫方法。但是如果要重寫的方法只有包範圍內纔可以訪問的,那麼使用replaced-method方法是一個非常不錯的選擇。

<replaced-method>的作用是重寫bean中的某個方法。在parseBeanDefinitionElement方法中調用BeanDefinitionParserDelegate的parseLookupOverrideSubElements方法處理<bean>標籤的子標籤<replaced-method>,parseReplacedMethodSubElements方法的源碼如下。

    public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {
        NodeList nl = beanEle.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (isCandidateElement(node) && nodeNameEquals(node, "replaced-method")) {
                Element replacedMethodEle = (Element) node;
                // 將被替換的方法名稱
                String name = replacedMethodEle.getAttribute("name");
                // 一個實現了MethodReplacer接口的bean名稱
                String callback = replacedMethodEle.getAttribute("replacer");
                ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);
                // 搜索<replaced-method>的<arg-type>子標籤
                // <arg-type>標籤用於指定方法的參數類型
                List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, "arg-type");
                for (Element argTypeEle : argTypeEles) {
                    // 獲取<arg-type>的值,這個值表示參數類型的全名稱
                    String match = argTypeEle.getAttribute("match");
                    match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));
                    if (StringUtils.hasText(match)) {
                        replaceOverride.addTypeIdentifier(match);
                    }
                }
                replaceOverride.setSource(extractSource(replacedMethodEle));
                overrides.addOverride(replaceOverride);
            }
        }
    }

1.6 解析<constructor-arg>標籤

<constructor-arg>用於指定使用帶參構造器來實例化bean的場景。

在parseBeanDefinitionElement方法中調用BeanDefinitionParserDelegate的parseConstructorArgElements方法處理<bean>標籤的子標籤<constructor-arg>,parseConstructorArgElements方法的源碼如下。

    public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
        NodeList nl = beanEle.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (isCandidateElement(node) && nodeNameEquals(node, "constructor-arg")) {
                parseConstructorArgElement((Element) node, bd);
            }
        }
    }
    public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
        // 獲取參數索引
        String indexAttr = ele.getAttribute("index");
        // 獲取參數類型字符串
        String typeAttr = ele.getAttribute("type");
        // 獲取參數名稱
        String nameAttr = ele.getAttribute("name");
        if (StringUtils.hasLength(indexAttr)) {
            // 配置了index屬性
            try {
                int index = Integer.parseInt(indexAttr);
                if (index < 0) {
                    error("'index' cannot be lower than 0", ele);
                } else {
                    try {
                        this.parseState.push(new ConstructorArgumentEntry(index));
                        // 獲取參數值
                        Object value = parsePropertyValue(ele, bd, null);
                        ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
                        if (StringUtils.hasLength(typeAttr)) {
                            valueHolder.setType(typeAttr);
                        }
                        if (StringUtils.hasLength(nameAttr)) {
                            valueHolder.setName(nameAttr);
                        }
                        valueHolder.setSource(extractSource(ele));
                        if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {
                            error("Ambiguous constructor-arg entries for index " + index, ele);
                        } else {
                            bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
                        }
                    } finally {
                        this.parseState.pop();
                    }
                }
            } catch (NumberFormatException ex) {
                error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);
            }
        } else {
            // 沒配置index屬性
            try {
                this.parseState.push(new ConstructorArgumentEntry());
                // 獲取參數值
                Object value = parsePropertyValue(ele, bd, null);
                ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
                if (StringUtils.hasLength(typeAttr)) {
                    valueHolder.setType(typeAttr);
                }
                if (StringUtils.hasLength(nameAttr)) {
                    valueHolder.setName(nameAttr);
                }
                valueHolder.setSource(extractSource(ele));
                bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
            } finally {
                this.parseState.pop();
            }
        }
    }

parseConstructorArgElement方法最重要的是通過調用BeanDefinitionParserDelegate的parsePropertyValue方法來獲取參數值,parsePropertyValue方法的代碼如下。

    public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {
        String elementName = (propertyName != null) ?
                        "<property> element for property '" + propertyName + "'" :
                        "<constructor-arg> element";

        // 除了<description>和<meta>標籤外,<property>和<constructor-arg>只允許存在一個用於獲取屬性或參數值的標籤,
        // 比如ref, value, list, props, set, array, map, bean等
        NodeList nl = ele.getChildNodes();
        Element subElement = null;
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (node instanceof Element && !nodeNameEquals(node, "description") &&
                    !nodeNameEquals(node, "meta")) {
                // 檢查是否有多個值子標籤
                if (subElement != null) {
                    error(elementName + " must not contain more than one sub-element", ele);
                } else {
                    subElement = (Element) node;
                }
            }
        }

        boolean hasRefAttribute = ele.hasAttribute("ref");
        boolean hasValueAttribute = ele.hasAttribute("value");
        // ref和value屬性不能同時設置
        // 有了ref或者value屬性值,不能再添加值標籤
        if ((hasRefAttribute && hasValueAttribute) ||
                ((hasRefAttribute || hasValueAttribute) && subElement != null)) {
            error(elementName +
                    " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
        }

        if (hasRefAttribute) {
            String refName = ele.getAttribute("ref");
            // ref屬性不能爲空
            if (!StringUtils.hasText(refName)) {
                error(elementName + " contains empty 'ref' attribute", ele);
            }
            // 創建並返回RuntimeBeanReference對象
            RuntimeBeanReference ref = new RuntimeBeanReference(refName);
            ref.setSource(extractSource(ele));
            return ref;
        } else if (hasValueAttribute) {
            // 創建並返回TypedStringValue對象
            TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute("value"));
            valueHolder.setSource(extractSource(ele));
            return valueHolder;
        }else if (subElement != null) {
            return parsePropertySubElement(subElement, bd);
        } else {
            // 參數和屬性必須要有值
            error(elementName + " must specify a ref or value", ele);
            return null;
        }
    }

parsePropertyValue方法主要是處理給定節點元素上的ref和value屬性值,如果沒有ref或者value屬性,則調用BeanDefinitionParserDelegate的parsePropertySubElement方法從<property>、<constroctor-arg>標籤的非meta非description子標籤中獲取值。下面是parsePropertySubElement方法的源代碼。

    public Object parsePropertySubElement(Element ele, BeanDefinition bd) {
        return parsePropertySubElement(ele, bd, null);
    }
    public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) {
        if (!isDefaultNamespace(ele)) {
            // 處理非默認命名空間中的<property>子標籤
            return parseNestedCustomElement(ele, bd);
        } else if (nodeNameEquals(ele, "bean")) {
            // 處理<property>下的bean標籤
            BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
            if (nestedBd != null) {
                nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
            }
            return nestedBd;
        } else if (nodeNameEquals(ele, "ref")) {
            // 處理<ref>標籤
            String refName = ele.getAttribute("bean");
            boolean toParent = false;
            if (!StringUtils.hasLength(refName)) {
                // A reference to the id of another bean in the same XML file.
                // 一個bean名稱,這個bean來自同一個xml文件
                refName = ele.getAttribute("local");
                if (!StringUtils.hasLength(refName)) {
                    // 一個bean名稱,這個bean來自於父類
                    refName = ele.getAttribute("parent");
                    toParent = true;
                    if (!StringUtils.hasLength(refName)) {
                        error("'bean', 'local' or 'parent' is required for <ref> element", ele);
                        return null;
                    }
                }
            }
            if (!StringUtils.hasText(refName)) {
                error("<ref> element contains empty target attribute", ele);
                return null;
            }
            RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
            ref.setSource(extractSource(ele));
            return ref;
        } else if (nodeNameEquals(ele, "idref")) {
            // 處理<idref>標籤
            return parseIdRefElement(ele);
        } else if (nodeNameEquals(ele, "value")) {
            // 處理<value>標籤
            return parseValueElement(ele, defaultValueType);
        } else if (nodeNameEquals(ele, "null")) {
            // 返回<null>標籤代表的null值,並用TypedStringValue對象來包裝
            TypedStringValue nullHolder = new TypedStringValue(null);
            nullHolder.setSource(extractSource(ele));
            return nullHolder;
        } else if (nodeNameEquals(ele, "array")) {
            // 處理<array>標籤
            return parseArrayElement(ele, bd);
        } else if (nodeNameEquals(ele, "list")) {
            // 處理<list>標籤
            return parseListElement(ele, bd);
        } else if (nodeNameEquals(ele, "set")) {
            // 處理<set>標籤
            return parseSetElement(ele, bd);
        } else if (nodeNameEquals(ele, "map")) {
            // 處理<map>標籤
            return parseMapElement(ele, bd);
        } else if (nodeNameEquals(ele, "props")) {
            // 處理<props>標籤
            return parsePropsElement(ele);
        } else {
            // 值標籤必須要提供一個值
            error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
            return null;
        }
    }

parsePropertySubElement方法處理<property>和<constructor-arg>標籤下的bean,ref, idref, value, null, array, list, set, map, props以及其他命名空間中的標籤,並返回這些標籤所代表的值。其中bean、ref和null標籤的處理已經這個方法中體現了,下面我們來探討剩下7個標籤如何處理的(這節不會討論其他命名空間的標籤如何處理)。

(1) 處理<idref>標籤

<idref>標籤不常用,一般只用於注入容器中某個bean的名稱或者id。它和<property>的value屬性以及<value>標籤值一樣,只是<idref>的bean、local屬性值必須爲存在於容器中bean的id或name屬性值一致,否則會拋異常。

parsePropertySubElement方法調用BeanDefinitionParserDelegate的parseIdRefElement方法來處理<idref>標籤,parseIdRefElement方法代碼如下。

    public Object parseIdRefElement(Element ele) {
        // 獲取bean屬性值
        String refName = ele.getAttribute("bean");
        if (!StringUtils.hasLength(refName)) {
            // 獲取local屬性值,該值與同一個xml文件中某個bean的名稱相同
            refName = ele.getAttribute("local");
            if (!StringUtils.hasLength(refName)) {
                error("Either 'bean' or 'local' is required for <idref> element", ele);
                return null;
            }
        }
        if (!StringUtils.hasText(refName)) {
            error("<idref> element contains empty target attribute", ele);
            return null;
        }
        RuntimeBeanNameReference ref = new RuntimeBeanNameReference(refName);
        ref.setSource(extractSource(ele));
        return ref;
    }

(2) 處理<value>標籤

<value>標籤用於指定一個字面值,可以是基本類型,字符串。

parsePropertySubElement方法調用BeanDefinitionParserDelegate的parseValueElement方法來處理<value>標籤,parseValueElement方法代碼如下。

    public Object parseValueElement(Element ele, String defaultTypeName) {
        // 獲取<value>標籤的字面值
        String value = DomUtils.getTextValue(ele);
        // 獲取type屬性,表示屬性值的類型的全名稱
        String specifiedTypeName = ele.getAttribute("type");
        String typeName = specifiedTypeName;
        if (!StringUtils.hasText(typeName)) {
            typeName = defaultTypeName;
        }
        try {
            // 根據value值和值類型創建TypedStringValue對象
            TypedStringValue typedValue = buildTypedStringValue(value, typeName);
            typedValue.setSource(extractSource(ele));
            typedValue.setSpecifiedTypeName(specifiedTypeName);
            return typedValue;
        } catch (ClassNotFoundException ex) {
            error("Type class [" + typeName + "] not found for <value> element", ele, ex);
            return value;
        }
    }

parseValueElement方法獲取<value>標籤的字面值和type屬性後通過buildTypedStringValue方法創建TypedStringValue 對象,buildTypedStringValue方法的代碼如下。

    protected TypedStringValue buildTypedStringValue(String value, String targetTypeName)
            throws ClassNotFoundException {

        ClassLoader classLoader = this.readerContext.getBeanClassLoader();
        TypedStringValue typedValue;
        if (!StringUtils.hasText(targetTypeName)) {
            typedValue = new TypedStringValue(value);
        } else if (classLoader != null) {
            // 通過ClassLoader對象來加載一個Class對象
            Class<?> targetType = ClassUtils.forName(targetTypeName, classLoader);
            typedValue = new TypedStringValue(value, targetType);
        } else {
            typedValue = new TypedStringValue(value, targetTypeName);
        }
        return typedValue;
    }

(3) 處理<array>標籤

<array>標籤用於指定一個數組對象。

parsePropertySubElement方法調用BeanDefinitionParserDelegate的parseArrayElement方法來處理<array>標籤,parseArrayElement方法代碼如下。

    public Object parseArrayElement(Element arrayEle, BeanDefinition bd) {
        // 獲取元素的value-type屬性值,它表示元素的類型
        String elementType = arrayEle.getAttribute("value-type");

        NodeList nl = arrayEle.getChildNodes();
        ManagedArray target = new ManagedArray(elementType, nl.getLength());
        target.setSource(extractSource(arrayEle));
        target.setElementTypeName(elementType);
        // 獲取並設置merge屬性值
        target.setMergeEnabled(parseMergeAttribute(arrayEle));

        // 處理子標籤
        parseCollectionElements(nl, target, bd, elementType);
        return target;
    }
    /**
    * 獲取元素的merge屬性值
    **/
    public boolean parseMergeAttribute(Element collectionElement) {
        String value = collectionElement.getAttribute("merge");
        if ("default".equals(value)) {
            value = this.defaults.getMerge();
        }
        return "true".equals(value);
    }

parseArrayElement方法主要是獲取<array>標籤中的value-type屬性值和merge屬性值來創建一個實現了Collection接口的ManagedArray對象,然後調用BeanDefinitionParserDelegate中處理集合元素子標籤的parseCollectionElements方法,這個方法的源碼如下。

    protected void parseCollectionElements(
            NodeList elementNodes, Collection<Object> target, BeanDefinition bd, String defaultElementType) {

        for (int i = 0; i < elementNodes.getLength(); i++) {
            Node node = elementNodes.item(i);
            if (node instanceof Element && !nodeNameEquals(node, "description")) {
                // 遞歸調用parsePropertySubElement方法來解析子節點。
                target.add(parsePropertySubElement((Element) node, bd, defaultElementType));
            }
        }
    }

parseCollectionElements方法遍歷集合標籤(array、set和list)下的子標籤,並遞歸調用處理<property>、<constructor-arg>標籤的子標籤的parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType)方法。

(4) 處理<list>標籤

<list>標籤用於指定一個List對象

parsePropertySubElement方法調用BeanDefinitionParserDelegate的parseListElement方法來處理<list>標籤,parseListElement方法代碼如下。

    public List<Object> parseListElement(Element collectionEle, BeanDefinition bd) {

        // 獲取元素的value-type屬性值,它表示元素的類型
        String defaultElementType = collectionEle.getAttribute("value-type");

        NodeList nl = collectionEle.getChildNodes();
        ManagedList<Object> target = new ManagedList<Object>(nl.getLength());
        target.setSource(extractSource(collectionEle));
        target.setElementTypeName(defaultElementType);
        // 獲取並設置merge屬性值
        target.setMergeEnabled(parseMergeAttribute(collectionEle));

        // 處理子標籤
        parseCollectionElements(nl, target, bd, defaultElementType);
        return target;
    }

parseListElement方法主要是獲取<list>標籤中的value-type屬性值和merge屬性值來創建一個實現了Collection接口的ManagedList對象,然後調用BeanDefinitionParserDelegate中處理集合元素子標籤的parseCollectionElements方法。

(5) 處理<set>標籤

<set>標籤用於指定一個Set對象。

parsePropertySubElement方法調用BeanDefinitionParserDelegate的parseSetElement方法來處理<set>標籤,parseSetElement方法代碼如下。

    public Set<Object> parseSetElement(Element collectionEle, BeanDefinition bd) {

        // 獲取元素的value-type屬性值,它表示元素的類型
        String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);

        NodeList nl = collectionEle.getChildNodes();
        ManagedSet<Object> target = new ManagedSet<Object>(nl.getLength());
        target.setSource(extractSource(collectionEle));
        target.setElementTypeName(defaultElementType);
        // 獲取並設置merge屬性值
        target.setMergeEnabled(parseMergeAttribute(collectionEle));

        // 處理子標籤
        parseCollectionElements(nl, target, bd, defaultElementType);
        return target;
    }

parseSetElement方法主要是獲取<set>標籤中的value-type屬性值和merge屬性值來創建一個實現了Collection接口的ManagedSet對象,然後調用BeanDefinitionParserDelegate中處理集合元素子標籤的parseCollectionElements方法。

(6) 處理<map>標籤

<map>標籤用於指定一個Map對象。

parsePropertySubElement方法調用BeanDefinitionParserDelegate的parseMapElement方法來處理<map>標籤,parseMapElement方法代碼如下。

    public Map<Object, Object> parseMapElement(Element mapEle, BeanDefinition bd) {
        // 獲取Map中key值的類型
        String defaultKeyType = mapEle.getAttribute("key-type");
        // 獲取Map中value值得類型
        String defaultValueType = mapEle.getAttribute("value-type");

        // 獲取<map>標籤下的所有<entry>子標籤
        List<Element> entryEles = DomUtils.getChildElementsByTagName(mapEle, "entry");
        ManagedMap<Object, Object> map = new ManagedMap<Object, Object>(entryEles.size());
        map.setSource(extractSource(mapEle));
        map.setKeyTypeName(defaultKeyType);
        map.setValueTypeName(defaultValueType);
        // 獲取並設置merge屬性值
        map.setMergeEnabled(parseMergeAttribute(mapEle));

        for (Element entryEle : entryEles) {
            NodeList entrySubNodes = entryEle.getChildNodes();
            Element keyEle = null;
            Element valueEle = null;
            // 獲取<entry>標籤下的<key>和值標籤
            for (int j = 0; j < entrySubNodes.getLength(); j++) {
                Node node = entrySubNodes.item(j);
                if (node instanceof Element) {
                    Element candidateEle = (Element) node;
                    if (nodeNameEquals(candidateEle, "key")) {
                        // 獲取key標籤
                        if (keyEle != null) {
                            error("<entry> element is only allowed to contain one <key> sub-element", entryEle);
                        } else {
                            keyEle = candidateEle;
                        }
                    } else {
                        // 獲取值標籤
                        if (nodeNameEquals(candidateEle, "description")) {
                            // 忽略<description>
                        } else if (valueEle != null) {
                            error("<entry> element must not contain more than one value sub-element", entryEle);
                        } else {
                            valueEle = candidateEle;
                        }
                    }
                }
            }

            // 從<entry>的屬性key或者key-ref或者子標籤<key>獲取Map中元素的key值
            Object key = null;
            boolean hasKeyAttribute = entryEle.hasAttribute("key");
            boolean hasKeyRefAttribute = entryEle.hasAttribute("key-ref");
            // 相同意義的標籤和屬性不能共存
            if ((hasKeyAttribute && hasKeyRefAttribute) ||
                    ((hasKeyAttribute || hasKeyRefAttribute)) && keyEle != null) {
                error("<entry> element is only allowed to contain either " +
                        "a 'key' attribute OR a 'key-ref' attribute OR a <key> sub-element", entryEle);
            }
            if (hasKeyAttribute) {
                // 處理屬性key值
                key = buildTypedStringValueForMap(entryEle.getAttribute("key"), defaultKeyType, entryEle);
            } else if (hasKeyRefAttribute) {
                // 處理屬性key-ref值
                String refName = entryEle.getAttribute("key-ref");
                if (!StringUtils.hasText(refName)) {
                    error("<entry> element contains empty 'key-ref' attribute", entryEle);
                }
                RuntimeBeanReference ref = new RuntimeBeanReference(refName);
                ref.setSource(extractSource(entryEle));
                key = ref;
            } else if (keyEle != null) {
                // 處理<key>標籤
                key = parseKeyElement(keyEle, bd, defaultKeyType);
            } else {
                error("<entry> element must specify a key", entryEle);
            }

            // 從<entry>的屬性value或者value-ref或者子值標籤(value,array,bean,map等)獲取Map中元素的value值
            Object value = null;
            boolean hasValueAttribute = entryEle.hasAttribute("value");
            boolean hasValueRefAttribute = entryEle.hasAttribute("value-ref");
            boolean hasValueTypeAttribute = entryEle.hasAttribute("value-type");
            // 相同意義的標籤和屬性不能共存
            // value、value-ref、值子標籤不能同時存在
            if ((hasValueAttribute && hasValueRefAttribute) ||
                    ((hasValueAttribute || hasValueRefAttribute)) && valueEle != null) {
                error("<entry> element is only allowed to contain either " +
                        "'value' attribute OR 'value-ref' attribute OR <value> sub-element", entryEle);
            }
            // 有value-type屬性值,則必須有value屬性值,且不能有value-ref值和子值標籤
            if ((hasValueTypeAttribute && hasValueRefAttribute) ||
                (hasValueTypeAttribute && !hasValueAttribute) ||
                    (hasValueTypeAttribute && valueEle != null)) {
                error("<entry> element is only allowed to contain a 'value-type' " +
                        "attribute when it has a 'value' attribute", entryEle);
            }
            if (hasValueAttribute) {
                // 處理value和value-type屬性值
                String valueType = entryEle.getAttribute("value-type");
                if (!StringUtils.hasText(valueType)) {
                    valueType = defaultValueType;
                }
                value = buildTypedStringValueForMap(entryEle.getAttribute("value"), valueType, entryEle);
            } else if (hasValueRefAttribute) {
                // 處理value-ref屬性值
                String refName = entryEle.getAttribute("value-ref");
                if (!StringUtils.hasText(refName)) {
                    error("<entry> element contains empty 'value-ref' attribute", entryEle);
                }
                RuntimeBeanReference ref = new RuntimeBeanReference(refName);
                ref.setSource(extractSource(entryEle));
                value = ref;
            } else if (valueEle != null) {
                // 處理值標籤,遞歸調用parsePropertySubElement方法
                value = parsePropertySubElement(valueEle, bd, defaultValueType);
            } else {
                error("<entry> element must specify a value", entryEle);
            }

            map.put(key, value);
        }

        return map;
    }
    /**
    * 處理<entry>上的屬性key或value值。返回一個TypedStringValue對象
    **/
    protected final Object buildTypedStringValueForMap(String value, String defaultTypeName, Element entryEle) {
        try {
            // 創建TypedStringValue對象
            TypedStringValue typedValue = buildTypedStringValue(value, defaultTypeName);
            typedValue.setSource(extractSource(entryEle));
            return typedValue;
        } catch (ClassNotFoundException ex) {
            error("Type class [" + defaultTypeName + "] not found for Map key/value type", entryEle, ex);
            return value;
        }
    }
    /**
    * 處理entry標籤下的key標籤
    **/
    protected Object parseKeyElement(Element keyEle, BeanDefinition bd, String defaultKeyTypeName) {
        NodeList nl = keyEle.getChildNodes();
        Element subElement = null;
        // 遍歷<key>標籤下的子標籤
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (node instanceof Element) {
                if (subElement != null) {
                    error("<key> element must not contain more than one value sub-element", keyEle);
                } else {
                    subElement = (Element) node;
                }
            }
        }
        return parsePropertySubElement(subElement, bd, defaultKeyTypeName);
    }

(7) 處理<props>標籤

<props>標籤用於指定一個Property對象。

parsePropertySubElement方法調用BeanDefinitionParserDelegate的parsePropsElement方法來處理<props>標籤,parsePropsElement方法代碼如下。

    public Properties parsePropsElement(Element propsEle) {

        ManagedProperties props = new ManagedProperties();
        props.setSource(extractSource(propsEle));
        props.setMergeEnabled(parseMergeAttribute(propsEle));

        // 獲取並遍歷所有的<prop>標籤
        List<Element> propEles = DomUtils.getChildElementsByTagName(propsEle, "prop");
        for (Element propEle : propEles) {
            // 獲取<prop>標籤的key屬性值
            String key = propEle.getAttribute("key");
            // 以<prop>標籤的字面值爲value值
            String value = DomUtils.getTextValue(propEle).trim();

            // 用key值創建TypedStringValue對象
            TypedStringValue keyHolder = new TypedStringValue(key);
            keyHolder.setSource(extractSource(propEle));

            // 用value值創建TypedStringValue
            TypedStringValue valueHolder = new TypedStringValue(value);
            valueHolder.setSource(extractSource(propEle));

            props.put(keyHolder, valueHolder);
        }

        return props;
    }

parsePropsElement方法遍歷並獲取<props>標籤下的所有<prop>標籤的key值和字面值。

1.7 解析<property>標籤

在parseBeanDefinitionElement方法中調用BeanDefinitionParserDelegate的parsePropertyElements方法處理<bean>標籤的子標籤<property>,parsePropertyElements方法的源碼如下。

    public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
        NodeList nl = beanEle.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            // 查找<property>標籤
            if (isCandidateElement(node) && nodeNameEquals(node, "property")) {
                parsePropertyElement((Element) node, bd);
            }
        }
    }

parsePropertyElements方法主要是搜索bean標籤下的<property>標籤,每找到一個<property>標籤就調用parsePropertyElement方法來處理此標籤,parsePropertyElement方法代碼如下。

    public void parsePropertyElement(Element ele, BeanDefinition bd) {
        // 獲取name屬性值,這值必須與bean中的屬性名稱相同
        String propertyName = ele.getAttribute("name");
        if (!StringUtils.hasLength(propertyName)) {
            error("Tag 'property' must have a 'name' attribute", ele);
            return;
        }
        this.parseState.push(new PropertyEntry(propertyName));
        try {
            if (bd.getPropertyValues().contains(propertyName)) {
                error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
                return;
            }
            // 獲取屬性值
            Object val = parsePropertyValue(ele, bd, propertyName);
            // 創建PropertyValue對象
            PropertyValue pv = new PropertyValue(propertyName, val);
            parseMetaElements(ele, pv);
            pv.setSource(extractSource(ele));
            // 保存PropertyValue對象
            bd.getPropertyValues().addPropertyValue(pv);
        } finally {
            this.parseState.pop();
        }
    }

和處理<constructor-arg>標籤一樣,parsePropertyElement方法也是調用BeanDefinitionParserDelegate的parsePropertyValue方法來獲取對應的值。

1.8 解析<qualifier>標籤

在parseBeanDefinitionElement方法中調用BeanDefinitionParserDelegate的parseQualifierElements方法處理<bean>標籤的子標籤<qualifier>,parseQualifierElements方法的源碼如下。

    public void parseQualifierElements(Element beanEle, AbstractBeanDefinition bd) {
        NodeList nl = beanEle.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (isCandidateElement(node) && nodeNameEquals(node, "qualifier")) {
                parseQualifierElement((Element) node, bd);
            }
        }
    }

parseQualifierElements方法主要是搜索並處理<bean>標籤下所有的<qualifier>標籤,沒搜索到一個<qualifier>標籤,就會調用BeanDefinitionParserDelegate的parseQualifierElement方法來處理,parseQualifierElement方法的源代碼如下。

    /**
    * 向BeanDefinition對象中添加AutowireCandidateQualifier對象
    **/
    public void parseQualifierElement(Element ele, AbstractBeanDefinition bd) {
        String typeName = ele.getAttribute("type");
        // <qualifier>標籤必須要有type屬性
        if (!StringUtils.hasLength(typeName)) {
            error("Tag 'qualifier' must have a 'type' attribute", ele);
            return;
        }
        this.parseState.push(new QualifierEntry(typeName));
        try {
            AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(typeName);
            qualifier.setSource(extractSource(ele));
            // 獲取<qualifier>的value屬性
            String value = ele.getAttribute("value");
            if (StringUtils.hasLength(value)) {
                qualifier.setAttribute("value", value);
            }
            NodeList nl = ele.getChildNodes();
            // 搜索<attribute>標籤
            for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                if (isCandidateElement(node) && nodeNameEquals(node, "attribute")) {
                    Element attributeEle = (Element) node;
                    // 獲取<attribute>標籤的key屬性值
                    String attributeName = attributeEle.getAttribute("key");
                    // 獲取<attribute>標籤的value屬性值
                    String attributeValue = attributeEle.getAttribute("value");
                    if (StringUtils.hasLength(attributeName) && StringUtils.hasLength(attributeValue)) {
                        BeanMetadataAttribute attribute = new BeanMetadataAttribute(attributeName, attributeValue);
                        attribute.setSource(extractSource(attributeEle));
                        qualifier.addMetadataAttribute(attribute);
                    } else {
                        // 定義了<attribute>標籤就必須要提供key和value屬性值
                        error("Qualifier 'attribute' tag must have a 'name' and 'value'", attributeEle);
                        return;
                    }
                }
            }
            bd.addQualifier(qualifier);
        } finally {
            this.parseState.pop();
        }
    }

<qualifier>標籤用於給bean創建限制標識符,其中type屬性爲一個註解類的全名稱,spring默認爲org.springframework.beans.factory.annotation.Qualifier;value值爲bean的標識符,可爲空;<attribute>標籤的key和value分別表示註解對象的方法名和方法返回值。

<qualifier>作用是區別相同類型的不同bean。個人覺得,它的好處是可以指定一個註解來代表一個bean,以免在代碼中hard-code bean的名稱,但所指定的註解必須要用@Qualifier註解標註。

qualifier的替代方案:我們可以用id屬性值或者name屬性值來替代<qualifier>標籤。同樣可以自定義一個註解類來代表一個bean,只是此時的@Qualifier的value方法的返回值必須與id屬性或者name屬性對應。本人覺得如果id屬性值或者name屬性值不會經常改變時,這比使用<qualifier>標籤更方便。

2. 裝飾BeanDefinition對象


DefaultBeanDefinitionDocumentReader對象的processBeanDefinition方法在調用BeanDefinitionParserDelegate對象的parseBeanDefinitionElement方法解析<bean>標籤並獲得持有BeanDefintion對象的BeanDefinitionHolder對象後,繼續調用BeanDefinitionParserDelegate對象的decorateBeanDefinitionIfRequired來對剛獲得BeanDefintion對象做進一步的加工處理。

這一部分的加工處理主要是處理<bean>標籤中非默認命名空間中的屬性或者子標籤,比如p:命名空間修飾的屬性。我們來看看BeanDefinitionParserDelegate的decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder)方法代碼,如下。

    public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder) {
        return decorateBeanDefinitionIfRequired(ele, definitionHolder, null);
    }
    public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
            Element ele, BeanDefinitionHolder definitionHolder, BeanDefinition containingBd) {

        BeanDefinitionHolder finalDefinition = definitionHolder;

        // 首先根據自定義屬性裝飾BeanDefinition
        // 比如http://www.springframework.org/schema/p命名空間的屬性
        NamedNodeMap attributes = ele.getAttributes();
        for (int i = 0; i < attributes.getLength(); i++) {
            Node node = attributes.item(i);
            finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
        }

        // 根據嵌套的自定義標籤元素裝飾BeanDefinition
        NodeList children = ele.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            Node node = children.item(i);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
            }
        }

        return finalDefinition;
    }

這段代碼的責任是遍歷標籤的屬性和子節點並調用decorateIfRequired(Node node, BeanDefinitionHolder originalDef, BeanDefinition containingBd)方法來處理屬性和<bean>的子標籤,詳見以下代碼。

    /**
    * 處理自定義命名空間標籤來裝飾BeanDefinition對象
    **/
    private BeanDefinitionHolder decorateIfRequired(
            Node node, BeanDefinitionHolder originalDef, BeanDefinition containingBd) {

        String namespaceUri = getNamespaceURI(node);
        if (!isDefaultNamespace(namespaceUri)) {
            // 根據節點所在的命名空間,獲取NamespaceHandler對象
            // 比如http://www.springframework.org/schema/p命名空間的爲SimplePropertyNamespaceHandler
            NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
            if (handler != null) {
                // 執行裝飾BeanDefinition對象
                return handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
            } else if (namespaceUri != null && namespaceUri.startsWith("http://www.springframework.org/")) {
                error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);
            } else {
                // 節點爲自定義命名空間的,但沒有指定NamespaceHandler 
                if (logger.isDebugEnabled()) {
                    logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");
                }
            }
        }
        return originalDef;
    }

decorateIfRequired方法只處理非默認命名空間的屬性和標籤,因此它首先會檢查節點是否爲默認明見中的,是默認空間的則直接返回,如果不是則調用NamespaceHandlerResolver對象(默認爲DefaultNamespaceHandlerResolver)來獲得節點所在命名空間的處理器NamespaceHandler對象,然後調用NamespaceHandler對象的decorate方法,並返回一個BeanDefinitionHolder 對象,它可以是新創建的,也可以是裝飾前的那個。

總結


(1) 可以通過給<beans>指定屬性值來全局性的設置<bean>標籤對應的屬性值,比如定義全局的初始化方法,則可以在<beans>定義default-init-method屬性值。

(2)<property>和<constructor-arg>標籤使用同一套屬性名和子標籤。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章