`

IoC容器解析Bean定义资源并注册解析后的Bean

阅读更多
《Spring技术内幕》学习笔记4——IoC容器解析Bean定义资源并注册解析后的Bean
1.对Spring IoC容器定位和载入Bean定义资源文件的基本过程,接下来我们要继续分析Spring IoC容器将载入的Bean定义资源文件转换为Document对象之后,是如何将其解析为Spring IoC管理的Bean对象并将其注册到容器中的。

2.XmlBeanDefinitionReader解析载入的Bean定义资源文件:

XmlBeanDefinitionReader类中的doLoadBeanDefinitions方法是从特定XML文件中实际载入Bean定义资源的方法,该方法在载入Bean定义资源之后将其转换为Document对象,接下来调用registerBeanDefinitions启动Spring IoC容器对Bean定义的解析过程,registerBeanDefinitions方法源码如下:

view plaincopy to clipboardprint?
//按照Spring的Bean语义要求将Bean定义资源解析并转换为容器内部数据结构 
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { 
        //得到BeanDefinitionDocumentReader来对xml格式的BeanDefinition解析 
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); 
        //获得容器中注册的Bean数量 
        int countBefore = getRegistry().getBeanDefinitionCount(); 
//解析过程入口,这里使用了委派模式,BeanDefinitionDocumentReader只是个接口,//具体的解析实现过程有实现类DefaultBeanDefinitionDocumentReader完成 
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); 
        //统计解析的Bean数量 
        return getRegistry().getBeanDefinitionCount() - countBefore; 
    } 
//创建BeanDefinitionDocumentReader对象,解析Document对象 
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() { 
        return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass)); 
    } 




Bean定义资源的载入解析分为以下两个过程:

首先,通过调用XML解析器将Bean定义资源文件转换得到Document对象,但是这些Document对象并没有按照Spring的Bean规则进行解析。这一步是载入的过程

其次,在完成通用的XML解析之后,按照Spring的Bean规则对Document对象进行解析。

按照Spring的Bean规则对Document对象解析的过程是在接口BeanDefinitionDocumentReader的实现类DefaultBeanDefinitionDocumentReader中实现的。



3.DefaultBeanDefinitionDocumentReader对Bean定义的Document对象解析:

BeanDefinitionDocumentReader接口通过registerBeanDefinitions方法调用其实现类DefaultBeanDefinitionDocumentReader对Document对象进行解析,解析的代码如下:

view plaincopy to clipboardprint?
//根据Spring DTD对Bean的定义规则解析Bean定义Document对象 
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { 
        //获得XML描述符 
        this.readerContext = readerContext; 
        logger.debug("Loading bean definitions"); 
        //获得Document的根元素 
        Element root = doc.getDocumentElement(); 
        //具体的解析过程由BeanDefinitionParserDelegate实现, 
//BeanDefinitionParserDelegate中定义了Spring Bean定义XML文件的各种元素 
        BeanDefinitionParserDelegate delegate = createHelper(readerContext, root); 
        //在解析Bean定义之前,进行自定义的解析,增强解析过程的可扩展性 
        preProcessXml(root); 
        //从Document的根元素开始进行Bean定义的Document对象 
        parseBeanDefinitions(root, delegate); 
        //在解析Bean定义之后,进行自定义的解析,增加解析过程的可扩展性 
postProcessXml(root); 
    } 
    //创建BeanDefinitionParserDelegate,用于完成真正的解析过程 
    protected BeanDefinitionParserDelegate createHelper(XmlReaderContext readerContext, Element root) { 
        BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext); 
        //BeanDefinitionParserDelegate初始化Document根元素 
        delegate.initDefaults(root); 
        return delegate; 
    } 
    //使用Spring的Bean规则从Document的根元素开始进行Bean定义的Document对象 
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { 
        //Bean定义的Document对象使用了Spring默认的XML命名空间 
        if (delegate.isDefaultNamespace(root)) { 
            //获取Bean定义的Document对象根元素的所有子节点 
            NodeList nl = root.getChildNodes(); 
            for (int i = 0; i < nl.getLength(); i++) { 
                Node node = nl.item(i); 
                //获得Document节点是XML元素节点 
                if (node instanceof Element) { 
                    Element ele = (Element) node; 
                //Bean定义的Document的元素节点使用的是Spring默认的XML命名空间 
                    if (delegate.isDefaultNamespace(ele)) { 
                        //使用Spring的Bean规则解析元素节点 
                        parseDefaultElement(ele, delegate); 
                    } 
                    else { 
//没有使用Spring默认的XML命名空间,则使用用户自定义的解//析规则解析元素节点 
                        delegate.parseCustomElement(ele); 
                    } 
                } 
            } 
        } 
        else { 
            //Document的根节点没有使用Spring默认的命名空间,则使用用户自定义的 
            //解析规则解析Document根节点 
            delegate.parseCustomElement(root); 
        } 
    } 
    //使用Spring的Bean规则解析Document元素节点 
    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { 
        //如果元素节点是<Import>导入元素,进行导入解析 
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { 
            importBeanDefinitionResource(ele); 
        } 
        //如果元素节点是<Alias>别名元素,进行别名解析 
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { 
            processAliasRegistration(ele); 
        } 
        //元素节点既不是导入元素,也不是别名元素,即普通的<Bean>元素, 
//按照Spring的Bean规则解析元素 
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { 
            processBeanDefinition(ele, delegate); 
        } 
    } 
    //解析<Import>导入元素,从给定的导入路径加载Bean定义资源到Spring IoC容器中 
    protected void importBeanDefinitionResource(Element ele) { 
        //获取给定的导入元素的location属性 
String location = ele.getAttribute(RESOURCE_ATTRIBUTE); 
        //如果导入元素的location属性值为空,则没有导入任何资源,直接返回 
if (!StringUtils.hasText(location)) { 
            getReaderContext().error("Resource location must not be empty", ele); 
            return; 
        } 
        //使用系统变量值解析location属性值 
        location = SystemPropertyUtils.resolvePlaceholders(location); 
        Set<Resource> actualResources = new LinkedHashSet<Resource>(4); 
//标识给定的导入元素的location是否是绝对路径 
        boolean absoluteLocation = false; 
        try { 
            absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute(); 
        } 
        catch (URISyntaxException ex) { 
            //给定的导入元素的location不是绝对路径 
        } 
        //给定的导入元素的location是绝对路径 
        if (absoluteLocation) { 
            try { 
                //使用资源读入器加载给定路径的Bean定义资源 
                int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources); 
                if (logger.isDebugEnabled()) { 
                    logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]"); 
                } 
            } 
            catch (BeanDefinitionStoreException ex) { 
                getReaderContext().error( 
                        "Failed to import bean definitions from URL location [" + location + "]", ele, ex); 
            } 
        } 
        else { 
            //给定的导入元素的location是相对路径 
            try { 
                int importCount; 
                //将给定导入元素的location封装为相对路径资源 
                Resource relativeResource = getReaderContext().getResource().createRelative(location); 
                //封装的相对路径资源存在 
                if (relativeResource.exists()) { 
                    //使用资源读入器加载Bean定义资源 
                    importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource); 
                    actualResources.add(relativeResource); 
                } 
                //封装的相对路径资源不存在 
                else { 
                    //获取Spring IoC容器资源读入器的基本路径 
                    String baseLocation = getReaderContext().getResource().getURL().toString(); 
                    //根据Spring IoC容器资源读入器的基本路径加载给定导入 
                    //路径的资源 
                    importCount = getReaderContext().getReader().loadBeanDefinitions( 
                            StringUtils.applyRelativePath(baseLocation, location), actualResources); 
                } 
                if (logger.isDebugEnabled()) { 
                    logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]"); 
                } 
            } 
            catch (IOException ex) { 
                getReaderContext().error("Failed to resolve current resource location", ele, ex); 
            } 
            catch (BeanDefinitionStoreException ex) { 
                getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]", 
                        ele, ex); 
            } 
        } 
        Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]); 
        //在解析完<Import>元素之后,发送容器导入其他资源处理完成事件 
        getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele)); 
    } 
    //解析<Alias>别名元素,为Bean向Spring IoC容器注册别名 
    protected void processAliasRegistration(Element ele) { 
        //获取<Alias>别名元素中name的属性值 
String name = ele.getAttribute(NAME_ATTRIBUTE); 
//获取<Alias>别名元素中alias的属性值 
        String alias = ele.getAttribute(ALIAS_ATTRIBUTE); 
        boolean valid = true; 
        //<alias>别名元素的name属性值为空 
if (!StringUtils.hasText(name)) { 
            getReaderContext().error("Name must not be empty", ele); 
            valid = false; 
        } 
        //<alias>别名元素的alias属性值为空 
        if (!StringUtils.hasText(alias)) { 
            getReaderContext().error("Alias must not be empty", ele); 
            valid = false; 
        } 
        if (valid) { 
            try { 
                //向容器的资源读入器注册别名 
                getReaderContext().getRegistry().registerAlias(name, alias); 
            } 
            catch (Exception ex) { 
                getReaderContext().error("Failed to register alias '" + alias + 
                        "' for bean with name '" + name + "'", ele, ex); 
            } 
            //在解析完<Alias>元素之后,发送容器别名处理完成事件 
            getReaderContext().fireAliasRegistered(name, alias, extractSource(ele)); 
        } 
    } 
    //解析Bean定义资源Document对象的普通元素 
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { 
        // BeanDefinitionHolder是对BeanDefinition的封装,即Bean定义的封装类 
//对Document对象中<Bean>元素的解析由BeanDefinitionParserDelegate实现  BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); 
        if (bdHolder != null) { 
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); 
            try { 
    //向Spring IoC容器注册解析得到的Bean定义,这是Bean定义向IoC容器注册的入口            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); 
            } 
            catch (BeanDefinitionStoreException ex) { 
                getReaderContext().error("Failed to register bean definition with name '" + 
                        bdHolder.getBeanName() + "'", ele, ex); 
            } 
            //在完成向Spring IoC容器注册解析得到的Bean定义之后,发送注册事件 
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); 
        } 
    } 




通过上述Spring IoC容器对载入的Bean定义Document解析可以看出,我们使用Spring时,在Spring配置文件中可以使用<Import>元素来导入IoC容器所需要的其他资源,Spring IoC容器在解析时会首先将指定导入的资源加载进容器中。使用<Ailas>别名时,Spring IoC容器首先将别名元素所定义的别名注册到容器中。

对于既不是<Import>元素,又不是<Alias>元素的元素,即Spring配置文件中普通的<Bean>元素的解析由BeanDefinitionParserDelegate类的parseBeanDefinitionElement方法来实现。

4.BeanDefinitionParserDelegate解析Bean定义资源文件中的<Bean>元素:

Bean定义资源文件中的<Import>和<Alias>元素解析在DefaultBeanDefinitionDocumentReader中已经完成,对Bean定义资源文件中使用最多的<Bean>元素交由BeanDefinitionParserDelegate来解析,其解析实现的源码如下:

view plaincopy to clipboardprint?
//解析<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; 
    } 




只要使用过Spring,对Spring配置文件比较熟悉的人,通过对上述源码的分析,就会明白我们在Spring配置文件中<Bean>元素的中配置的属性就是通过该方法解析和设置到Bean中去的。

注意:在解析<Bean>元素过程中没有创建和实例化Bean对象,只是创建了Bean对象的定义类BeanDefinition,将<Bean>元素中的配置信息设置到BeanDefinition中作为记录,当依赖注入时才使用这些记录信息创建和实例化具体的Bean对象。

上面方法中一些对一些配置如元信息(meta)、qualifier等的解析,我们在Spring中配置时使用的也不多,我们在使用Spring的<Bean>元素时,配置最多的是<property>属性,因此我们下面继续分析源码,了解Bean的属性在解析时是如何设置的。

5.BeanDefinitionParserDelegate解析<property>元素:

BeanDefinitionParserDelegate在解析<Bean>调用parsePropertyElements方法解析<Bean>元素中的<property>属性子元素,解析源码如下:

view plaincopy to clipboardprint?
//解析<Bean>元素中的<property>子元素 
public void parsePropertyElements(Element beanEle, BeanDefinition bd) { 
            //获取<Bean>元素中所有的子元素 
NodeList nl = beanEle.getChildNodes(); 
        for (int i = 0; i < nl.getLength(); i++) { 
            Node node = nl.item(i); 
            //如果子元素是<property>子元素,则调用解析<property>子元素方法解析 
            if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) { 
                parsePropertyElement((Element) node, bd); 
            } 
        } 
    } 
//解析<property>元素 
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的值 
            Object val = parsePropertyValue(ele, bd, propertyName); 
            //根据property的名字和值创建property实例 
            PropertyValue pv = new PropertyValue(propertyName, val); 
            //解析<property>元素中的属性 
            parseMetaElements(ele, pv); 
            pv.setSource(extractSource(ele)); 
            bd.getPropertyValues().addPropertyValue(pv); 
        } 
        finally { 
            this.parseState.pop(); 
        } 
    } 
//解析获取property值 
public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) { 
        String elementName = (propertyName != null) ? 
                        "<property> element for property '" + propertyName + "'" : 
                        "<constructor-arg> element"; 
        //获取<property>的所有子元素,只能是其中一种类型:ref,value,list等 
        NodeList nl = ele.getChildNodes(); 
        Element subElement = null; 
        for (int i = 0; i < nl.getLength(); i++) { 
            Node node = nl.item(i); 
            //子元素不是description和meta属性 
            if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) && 
                    !nodeNameEquals(node, META_ELEMENT)) { 
                if (subElement != null) { 
                    error(elementName + " must not contain more than one sub-element", ele); 
                } 
                else {//当前<property>元素包含有子元素 
                    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的数据对象是被当前的property对象所引用 
            ref.setSource(extractSource(ele)); 
            return ref; 
        } 
//如果属性是value,创建一个value的数据对象TypedStringValue,这个对象 
//封装了value信息 
        else if (hasValueAttribute) { 
            //一个持有String类型值的对象 
            TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE)); 
//设置这个value数据对象是被当前的property对象所引用 
            valueHolder.setSource(extractSource(ele)); 
            return valueHolder; 
        } 
        //如果当前<property>元素还有子元素 
        else if (subElement != null) { 
            //解析<property>的子元素 
            return parsePropertySubElement(subElement, bd); 
        } 
        else { 
            //propery属性中既不是ref,也不是value属性,解析出错返回null        error(elementName + " must specify a ref or value", ele); 
            return null; 
        } 
    } 




通过对上述源码的分析,我们可以了解在Spring配置文件中,<Bean>元素中<property>元素的相关配置是如何处理的:

a. ref被封装为指向依赖对象一个引用。

b.value配置都会封装成一个字符串类型的对象。

c.ref和value都通过“解析的数据类型属性值.setSource(extractSource(ele));”方法将属性值/引用与所引用的属性关联起来。

在方法的最后对于<property>元素的子元素通过parsePropertySubElement 方法解析,我们继续分析该方法的源码,了解其解析过程。

6.解析<property>元素的子元素:

在BeanDefinitionParserDelegate类中的parsePropertySubElement方法对<property>中的子元素解析,源码如下:

view plaincopy to clipboardprint?
//解析<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; 
        } 
    } 




通过上述源码分析,我们明白了在Spring配置文件中,对<property>元素中配置的Array、List、Set、Map、Prop等各种集合子元素的都通过上述方法解析,生成对应的数据对象,比如ManagedList、ManagedArray、ManagedSet等,这些Managed类是Spring对象BeanDefiniton的数据封装,对集合数据类型的具体解析有各自的解析方法实现,解析方法的命名非常规范,一目了然,我们对<list>集合元素的解析方法进行源码分析,了解其实现过程。

7.解析<list>子元素:

在BeanDefinitionParserDelegate类中的parseListElement方法就是具体实现解析<property>元素中的<list>集合子元素,源码如下:

view plaincopy to clipboardprint?
//解析<list>集合子元素 
public List parseListElement(Element collectionEle, BeanDefinition bd) { 
        //获取<list>元素中的value-type属性,即获取集合元素的数据类型 
        String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE); 
        //获取<list>集合元素中的所有子节点 
        NodeList nl = collectionEle.getChildNodes(); 
        //Spring中将List封装为ManagedList 
        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; 
    }  
//具体解析<list>集合元素,<array>、<list>和<set>都使用该方法解析 
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); 
            //节点不是description节点 
            if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT)) { 
                //将解析的元素加入集合中,递归调用下一个子元素 
                target.add(parsePropertySubElement((Element) node, bd, defaultElementType)); 
            } 
        } 
    }  




经过对Spring Bean定义资源文件转换的Document对象中的元素层层解析,Spring IoC现在已经将XML形式定义的Bean定义资源文件转换为Spring IoC所识别的数据结构——BeanDefinition,它是Bean定义资源文件中配置的POJO对象在Spring IoC容器中的映射,我们可以通过AbstractBeanDefinition为入口,荣IoC容器进行索引、查询和操作。

通过Spring IoC容器对Bean定义资源的解析后,IoC容器大致完成了管理Bean对象的准备工作,即初始化过程,但是最为重要的依赖注入还没有发生,现在在IoC容器中BeanDefinition存储的只是一些静态信息,接下来需要向容器注册Bean定义信息才能全部完成IoC容器的初始化过程。

8.解析过后的BeanDefinition在IoC容器中的注册:

让我们继续跟踪程序的执行顺序,接下来会到我们第3步中分析DefaultBeanDefinitionDocumentReader对Bean定义转换的Document对象解析的流程中,在其parseDefaultElement方法中完成对Document对象的解析后得到封装BeanDefinition的BeanDefinitionHold对象,然后调用BeanDefinitionReaderUtils的registerBeanDefinition方法向IoC容器注册解析的Bean,BeanDefinitionReaderUtils的注册的源码如下:

view plaincopy to clipboardprint?
//将解析的BeanDefinitionHold注册到容器中 
public static void registerBeanDefinition( 
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) 
            throws BeanDefinitionStoreException { 
        //获取解析的BeanDefinition的名称 
        String beanName = definitionHolder.getBeanName(); 
        //向IoC容器注册BeanDefinition 
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); 
        //如果解析的BeanDefinition有别名,向容器为其注册别名 
        String[] aliases = definitionHolder.getAliases(); 
        if (aliases != null) { 
            for (String aliase : aliases) { 
                registry.registerAlias(beanName, aliase); 
            } 
        } 
    }  




当调用BeanDefinitionReaderUtils向IoC容器注册解析的BeanDefinition时,真正完成注册功能的是DefaultListableBeanFactory。

9.DefaultListableBeanFactory向IoC容器注册解析后的BeanDefinition:

DefaultListableBeanFactory中使用一个HashMap的集合对象存放IoC容器中注册解析的BeanDefinition,向IoC容器注册的主要源码如下:

view plaincopy to clipboardprint?
//存储注册的俄BeanDefinition 
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(); 
//向IoC容器注册解析的BeanDefiniton 
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 
            throws BeanDefinitionStoreException { 
        Assert.hasText(beanName, "Bean name must not be empty"); 
        Assert.notNull(beanDefinition, "BeanDefinition must not be null"); 
        //校验解析的BeanDefiniton 
        if (beanDefinition instanceof AbstractBeanDefinition) { 
            try { 
                ((AbstractBeanDefinition) beanDefinition).validate(); 
            } 
            catch (BeanDefinitionValidationException ex) { 
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, 
                        "Validation of bean definition failed", ex); 
            } 
        } 
        //注册的过程中需要线程同步,以保证数据的一致性 
        synchronized (this.beanDefinitionMap) { 
            Object oldBeanDefinition = this.beanDefinitionMap.get(beanName); 
            //检查是否有同名的BeanDefinition已经在IoC容器中注册,如果已经注册, 
            //并且不允许覆盖已注册的Bean,则抛出注册失败异常 
            if (oldBeanDefinition != null) { 
                if (!this.allowBeanDefinitionOverriding) { 
                    throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, 
                            "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + 
                            "': There is already [" + oldBeanDefinition + "] bound."); 
                } 
                else {//如果允许覆盖,则同名的Bean,后注册的覆盖先注册的 
                    if (this.logger.isInfoEnabled()) { 
                        this.logger.info("Overriding bean definition for bean '" + beanName + 
                                "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); 
                    } 
                } 
            } 
            //IoC容器中没有已经注册同名的Bean,按正常注册流程注册 
            else { 
                this.beanDefinitionNames.add(beanName); 
                this.frozenBeanDefinitionNames = null; 
            } 
            this.beanDefinitionMap.put(beanName, beanDefinition); 
            //重置所有已经注册过的BeanDefinition的缓存 
            resetBeanDefinition(beanName); 
        } 
    } 



至此,Bean定义资源文件中配置的Bean被解析过后,已经注册到IoC容器中,被容器管理起来,真正完成了IoC容器初始化所做的全部工作。现在IoC容器中已经建立了整个Bean的配置信息,这些BeanDefinition信息已经可以使用,并且可以被检索,IoC容器的作用就是对这些注册的Bean定义信息进行处理和维护。这些的注册的Bean定义信息是IoC容器控制反转的基础,正是有了这些注册的数据,容器才可以进行依赖注入。
分享到:
评论

相关推荐

    Spring技术内幕学习笔记.docx

    《Spring技术内幕》学习笔记4——IoC容器解析Bean定义资源并注册解析后的Bean 《Spring技术内幕》学习笔记5——IoC容器的依赖注入 《Spring技术内幕》学习笔记6——IoC容器的高级特性 《Spring技术内幕》学习笔记7...

    IOC容器 mini-container

    mini-container ioc的一个开源实现,功能简单

    深入解析Spring IoC源码:核心机制与实践应用

    本文深入探讨了Spring框架中IoC容器的源码机制,涵盖了容器的初始化、Bean工厂的实例化、Bean定义的读取及Spring Bean的生命周期管理。通过精细的分析,本文揭示了AnnotationConfigApplicationContext的实例化过程,...

    spring源码解决

    //这里根据bean的名字,在IOC容器中得到bean实例,这个IOC容器就是一个大的抽象工厂。 Object getBean(String name) throws BeansException; //这里根据bean的名字和Class类型来得到bean实例,和上面的方法不同在于...

    spring源代码解析

    //对于XmlWebApplicationContext,当然使用的是XmlBeanDefinitionReader来对bean定义信息来进行解析 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); ...

    Spring源码学习三:BeanFactory解析1

    BeanFactory是用于访问Spring Bean容器的根接口,是一个单纯的Bean工厂,也就是常说的IOC容器的顶层定义,各种IOC容器是在其基础上为了满

    SpringIOC源码.rar

    1、 需要去构造一个Spring容器:ClassPathXmlApplicationContext; 2、 需要注解定义两个注解:@Service、@...5、 以键值对的方式创建beanID,bean对象,并且放到容器中; 6、 使用的时候通过beanID获取Bean对象。

    spring in action英文版

     2.4.2 对Bean工厂进行后处理  2.4.3 分散配置  2.4.4 定制属性编辑器  2.4.5 解析文本信息  2.4.6 监听事件  2.4.7 发布事件  2.4.8 感知其他Bean  2.5 小结  第3章 创建切面  3.1 ...

    Spring中文帮助文档

    3.6. bean定义的继承 3.7. 容器扩展点 3.7.1. 用BeanPostProcessor定制bean 3.7.2. 用BeanFactoryPostProcessor定制配置元数据 3.7.3. 使用FactoryBean定制实例化逻辑 3.8. The ApplicationContext 3.8.1. ...

    Spring-Reference_zh_CN(Spring中文参考手册)

    3.6. bean定义的继承 3.7. 容器扩展点 3.7.1. 用BeanPostProcessor定制bean 3.7.1.1. 使用BeanPostProcessor的Hello World示例 3.7.1.2. RequiredAnnotationBeanPostProcessor示例 3.7.2. 用...

    Spring API

    3.6. bean定义的继承 3.7. 容器扩展点 3.7.1. 用BeanPostProcessor定制bean 3.7.2. 用BeanFactoryPostProcessor定制配置元数据 3.7.3. 使用FactoryBean定制实例化逻辑 3.8. The ApplicationContext 3.8.1. ...

    spring chm文档

    3.6. bean定义的继承 3.7. 容器扩展点 3.7.1. 用BeanPostProcessor定制bean 3.7.2. 用BeanFactoryPostProcessor定制配置元数据 3.7.3. 使用FactoryBean定制实例化逻辑 3.8. ApplicationContext 3.8.1. 利用...

    Spring 2.0 开发参考手册

    3.6. bean定义的继承 3.7. 容器扩展点 3.7.1. 用BeanPostProcessor定制bean 3.7.2. 用BeanFactoryPostProcessor定制配置元数据 3.7.3. 使用FactoryBean定制实例化逻辑 3.8. ApplicationContext 3.8.1. 利用...

    Spring.3.x企业应用开发实战(完整版).part2

    4.11.1 使用Java类提供Bean定义信息 4.11.2 使用基于Java类的配置信息启动Spring容器 4.12 不同配置方式比较 4.13 小结 第5章 Spring容器高级主题 5.1 Spring容器技术内幕 5.1.1 内部工作机制 5.1.2 BeanDefinition ...

    Spring3.x企业应用开发实战(完整版) part1

    4.11.1 使用Java类提供Bean定义信息 4.11.2 使用基于Java类的配置信息启动Spring容器 4.12 不同配置方式比较 4.13 小结 第5章 Spring容器高级主题 5.1 Spring容器技术内幕 5.1.1 内部工作机制 5.1.2 BeanDefinition ...

    开源框架 Spring Gossip

    ApplicationContext 除了具备如 BeanFactory 基本的容器管理功能之外,并支援更多应用程式框架的特性,像是资源的取得、讯息解析、事件的处理与传播。 Resource 的取得 解析文字讯息 倾听事件 ...

    java面试题

    答:EJB不是一般的JavaBean,EJB是企业级的JavaBean,EJB一共分为3种,实体Bean,消息Bean,会话Bean。书写EJB要遵循一定的规范,另外要运行EJB,你需要有相应的EJB容器,比如WebLogic、jboss等,而JavaBean不需要...

    Spring面试题

    Spring即使一个AOP框架,也是一IOC容器。 Spring 最好的地方是它有助于您替换对象。有了 Spring,只要用 JavaBean 属性和配置文件加入依赖性(协作对象)。然后可以很容易地在需要时替换具有类似接口的协作对象。} ...

    Spring攻略(第二版 中文高清版).part2

    2.11 创建Bean后处理器 85 2.11.1 问题 85 2.11.2 解决方案 85 2.11.3 工作原理 86 2.12 外部化Bean配置 89 2.12.1 问题 89 2.12.2 解决方案 89 2.12.3 工作原理 90 2.13 解析文本消息 91 2.13.1...

    Spring攻略(第二版 中文高清版).part1

    2.11 创建Bean后处理器 85 2.11.1 问题 85 2.11.2 解决方案 85 2.11.3 工作原理 86 2.12 外部化Bean配置 89 2.12.1 问题 89 2.12.2 解决方案 89 2.12.3 工作原理 90 2.13 解析文本消息 91 2.13.1...

Global site tag (gtag.js) - Google Analytics