Spring源码解读-Spring IoC容器初始化之资源解析

**上篇博客已经讲到,如何查找BeanDefinition信息(水源)的问题,这次我们要对信息进行解析,解析成容器认识的数据结构。 载入相当于把定义的BeanDefinition在IoC容器中转化成一个Spring内部表示的数据结构。IoC容器对Bean的管理和依赖注入功能的实现,是通过对其持有的BeanDefinition进行各种相关操作来完成的。
大家在写配置文件的时候,会有Bean这样的标签,但是怎么对Bean语义进行解析并转化成容器内部数据结构的,看下面的代码:**

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        //这里得到BeanDefinitionDocumentReader来对XML的BeanDefinition进行解析
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        documentReader.setEnvironment(this.getEnvironment());
        int countBefore = getRegistry().getBeanDefinitionCount();
        //具体解析过程是这个registerBeanDefinitions完成
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }

**BeanDefinition的载入分成两部分,首先通过调用XML的解析器得到document对象,但这些document对象并没有按照Spring的Bean规则进行解析,在完成通用的XML解析后,才是按照SPring的Bean规则进行解析。这个解析是在documentReader中实现,使用的是默认设置好的DefaultBeanDefinitionReader。
关于BeanDefinitionParserDelegate这个类的研究,包含了对各种Spring Bean定义规则的处理。比如熟悉的对Bean元素的处理是怎么完成的,也就是怎样处理在XML定义文件中出现的这个最常见的元素信息,还会有id,name,aliase等属性元素等,看下面的代码:**

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
        //这里取得在bean元素中定义的id、name、和aliase属性的值
        String id = ele.getAttribute(ID_ATTRIBUTE);
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
        List<String> aliases = new ArrayList<String>();
        if (StringUtils.hasLength(nameAttr)) {
            String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            aliases.addAll(Arrays.asList(nameArr));
        }

        String beanName = id;
        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");
            }
        }

        if (containingBean == null) {
            checkNameUniqueness(beanName, aliases, ele);
        }
        //这个方法会引发对Bean元素的详细解析
        AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
        if (beanDefinition != null) {
            if (!StringUtils.hasText(beanName)) {
                try {
                    if (containingBean != null) {
                        beanName = BeanDefinitionReaderUtils.generateBeanName(
                                beanDefinition, this.readerContext.getRegistry(), true);
                    }
                    else {
                        beanName = this.readerContext.generateBeanName(beanDefinition);
                        // Register an alias for the plain bean class name, if still possible,
                        // if the generator returned the class name plus a suffix.
                        // This is expected for Spring 1.2/2.0 backwards compatibility.
                        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);
        }

        return null;
    }

**谈到这,不得不提的是AbstractBeanDefinition这个类,这个类封装了许多数据,大多都是和相关的,也就是我们在定义bean时,看到的那些Spring标记。比如init-method、destroy-method等等,这些数据类型很重要,都是IoC容器需要的。有了这些数据类型才能对Bean配置进行处理。实现容器特性。再比如beanClass、description、lazyInit这些属性都是配置bean时用到的,BeanDefinition是IoC容器体系中非常重要的核心数据结构。通过解析后,这些数据就好在IoC容器里大显身手的准备了。
讲一下对元素attribute值的处理:**

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

        this.parseState.push(new BeanEntry(beanName));
        //这里只读取定义的<bean>中设置的class名字,然后载入到BeanDefinition中去,只是做个记录
        //并不涉及到对象实例化过程,对象实例化实际上是在依赖注入时完成的。
        String className = null;
        if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
            className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
        }

        try {
            String parent = null;
            if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
                parent = ele.getAttribute(PARENT_ATTRIBUTE);
            }
            //生成需要的BeanDefinition对象,为Bean定义信息的载入做准备
            AbstractBeanDefinition bd = createBeanDefinition(className, parent);
            //对当前的Bean元素进行属性解析,并设置description的信息
            parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
            bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
            //对各种<bean>元素信息进行解析
            parseMetaElements(ele, bd);

            parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
            parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
            //解析<bean>的构造函数设置
            parseConstructorArgElements(ele, bd);
            //解析<bean>的property设置
            parsePropertyElements(ele, bd);
            parseQualifierElements(ele, bd);

            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;
    }

上面的代码是具体生成BeanDefinition的地方,我们举个例子,对property进行解析的例子,来完成对整个BeanDefinition载入过程的分析。

//对指定的Bean元素的property子元素集合进行解析
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
        NodeList nl = beanEle.getChildNodes();
        //遍历所有Bean元素下定义的property元素
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
                //在判断是property元素后对该property元素进行解析的过程。
                parsePropertyElement((Element) node, bd);
            }
        }
    }
public void parsePropertyElement(Element ele, BeanDefinition bd) {
        //取得property的名字
        String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
        if (!StringUtils.hasLength(propertyName)) {
            error("Tag 'property' must have a 'name' attribute", ele);
            return;
        }
        this.parseState.push(new PropertyEntry(propertyName));
        try {
            //如果同一个bean中已经有同名的property存在,不解析,直接返回
            //也就是说,如果同一个bean中有同名的property设置,那么起作用的只有一个。
            if (bd.getPropertyValues().contains(propertyName)) {
                error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
                return;
            }
            //这是解析property值的地方,返回的对象对应对Bean定义的property属性设置的解析结
            //果,这个解析结果会封装到PropertyValue对象中,然后设置到BeanDefinitionHolder中
            Object val = parsePropertyValue(ele, bd, propertyName);
            PropertyValue pv = new PropertyValue(propertyName, val);
            parseMetaElements(ele, pv);
            pv.setSource(extractSource(ele));
            bd.getPropertyValues().addPropertyValue(pv);
        }
        finally {
            this.parseState.pop();
        }
    }
//这里取得property元素的值,也许是一个list或其他
public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {
        String elementName = (propertyName != null) ?
                        "<property> element for property '" + propertyName + "'" :
                        "<constructor-arg> element";

        // Should only have one child element: ref, value, list, etc.
        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_ELEMENT) &&
                    !nodeNameEquals(node, META_ELEMENT)) {
                // Child element is what we're looking for.
                if (subElement != null) {
                    error(elementName + " must not contain more than one sub-element", ele);
                }
                else {
                    subElement = (Element) node;
                }
            }
        }
        //这里判断property的属性,是ref还是value,不允许同时是ref和value
        boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
        boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
        if ((hasRefAttribute && hasValueAttribute) ||
                ((hasRefAttribute || hasValueAttribute) && subElement != null)) {
            error(elementName +
                    " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
        }
        //如果是ref,创建一个ref的数据对象RuntimeBeanReference,这个对象封装了ref信息
        if (hasRefAttribute) {
            String refName = ele.getAttribute(REF_ATTRIBUTE);
            if (!StringUtils.hasText(refName)) {
                error(elementName + " contains empty 'ref' attribute", ele);
            }
            RuntimeBeanReference ref = new RuntimeBeanReference(refName);
            ref.setSource(extractSource(ele));
            return ref;
        }
        //如果是value,创建一个value的数据对象TypedStringValue,这个对象封装了value的信息
        else if (hasValueAttribute) {
            TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
            valueHolder.setSource(extractSource(ele));
            return valueHolder;
        }
        //如果还有子元素,触发对子元素的解析
        else if (subElement != null) {
            return parsePropertySubElement(subElement, bd);
        }
        else {
            // Neither child element nor "ref" or "value" attribute found.
            error(elementName + " must specify a ref or value", ele);
            return null;
        }
    }

以上是对property资源的解析过程,Array,List,Set、Map、Prop等各种元素都会在这里进行解析,生成对应的数据对象。对List集合的解析看下面代码:

public List parseListElement(Element collectionEle, BeanDefinition bd) {
        String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);
        NodeList nl = collectionEle.getChildNodes();
        ManagedList<Object> target = new ManagedList<Object>(nl.getLength());
        target.setSource(extractSource(collectionEle));
        target.setElementTypeName(defaultElementType);
        target.setMergeEnabled(parseMergeAttribute(collectionEle));
        //具体解析list元素过程
        parseCollectionElements(nl, target, bd, defaultElementType);
        return target;
    }

protected void parseCollectionElements(
        NodeList elementNodes, Collection<Object> target, BeanDefinition bd, String defaultElementType) {
    //遍历所有的元素节点,并判断其类型是否为Element
    for (int i = 0; i < elementNodes.getLength(); i++) {
        Node node = elementNodes.item(i);
        if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT)) {
            //加入到target中,target是一个managedList,同时触发对下一层元素的解析过程。
            //这是一个递归的调用
            target.add(parsePropertySubElement((Element) node, bd, defaultElementType));
        }
    }
}

最后经过逐层的解析,在XML文件中定义的BeanDefinition就被载入到IoC容器了,在容器中建立了数据映射。但是重要的依赖注入实际上还没有发生,要完全发挥容器的作用,还需要完成容器的注册。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值