Spring源码解析 — 读取bean元数据

源码分析基于spring 4.3.x

本文通过阅读源码,分析spring如何读取xml配置中的bean数据

关于阅读源码的思路,可参考 — 如何阅读java源码

本文主要分析XmlBeanFactory,XmlBeanFactory是一个简单的容器, 从XML文件中读取配置元数据。

BeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("spring.xml"));

XmlBeanFactory已过时,但好在简单,便于分析源码。

XmlBeanFactory#构造函数 -> XmlBeanDefinitionReader#loadBeanDefinitions

-> XmlBeanDefinitionReader#doLoadBeanDefinitions

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
        throws BeanDefinitionStoreException {
    try {
        Document doc = doLoadDocument(inputSource, resource);    // #1
        return registerBeanDefinitions(doc, resource);    // #2
    }
    ...
}

#1 加载xml到Document类(使用org.w3c.dom读取xml文件)

#2 registerBeanDefinitions -> 解析xml数据

XmlBeanDefinitionReader#registerBeanDefinitions

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();    // #1
    int countBefore = getRegistry().getBeanDefinitionCount();
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));    // #2
    return getRegistry().getBeanDefinitionCount() - countBefore;
}

#1 构建BeanDefinitionDocumentReader

#2

createReaderContext -> 构建ReaderContext,ReaderContext中存放了xml的resource,XmlBeanDefinitionReader等信息,这些后面都要使用。

registerBeanDefinitions -> 使用BeanDefinitionDocumentReader解析xml数据

注意,ReaderContext中存放了XmlBeanDefinitionReader,而XmlBeanDefinitionReader中存放了bean注册器BeanDefinitionRegistry(后面注册BeanDefinition要使用)。这个注册器就是XmlBeanFactory,XmlBeanFactory中构建XmlBeanDefinitionReader时将this作为Registry参数

XmlBeanDefinitionReader#reader

private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);

XmlBeanFactory的父类DefaultListableBeanFactory实现了BeanDefinitionRegistry。

DefaultBeanDefinitionDocumentReader#registerBeanDefinitions -> DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions

protected void doRegisterBeanDefinitions(Element root) {
    BeanDefinitionParserDelegate parent = this.delegate;
    this.delegate = createDelegate(getReaderContext(), root, parent);

    if (this.delegate.isDefaultNamespace(root)) {    // #1
        String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);    // #2
        if (StringUtils.hasText(profileSpec)) {     
            String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                    profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                if (logger.isInfoEnabled()) {
                    logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                            "] not matching: " + getReaderContext().getResource());
                }
                return;
            }
        }
    }

    preProcessXml(root);    // #3
    parseBeanDefinitions(root, this.delegate);    // #4
    postProcessXml(root);

    this.delegate = parent;
}

#1 判断元素的命名空间是否为spring的beans空间,这个方法后面也有使用

#2 处理PROFILE元素属性

#3 预处理,为子类提供的扩展方法

#4 parseBeanDefinitions -> 解析beans元素

DefaultBeanDefinitionDocumentReader#parseBeanDefinitions

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    if (delegate.isDefaultNamespace(root)) {
        NodeList nl = root.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (node instanceof Element) {
                Element ele = (Element) node;
                if (delegate.isDefaultNamespace(ele)) {    
                    parseDefaultElement(ele, delegate);    // #1
                }
                else {
                    delegate.parseCustomElement(ele);    // #2
                }
            }
        }
    }
    else {
        delegate.parseCustomElement(root);
    }
}

#1 对于beans命名空间的元素,使用默认的方法进行解析

#2 处理非beans命名空间的元素,使用对应的解析器处理

用户也可以自定义标签及标签解析器。

DefaultBeanDefinitionDocumentReader#parseDefaultElement

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {    // #1
            importBeanDefinitionResource(ele);
        }
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {    // #2
            processAliasRegistration(ele);
        }
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {    // #3
            processBeanDefinition(ele, delegate);
        }
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {    // #4
            doRegisterBeanDefinitions(ele);
        }
    }

#1 解析import元素

#2 解析alias元素

#3 解析bean元素

#4 解析beans元素

主要看对bean元素的解析,DefaultBeanDefinitionDocumentReader#processBeanDefinition

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);    // #1
        if (bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);    // #2
            try {
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());    // #3
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" +
                        bdHolder.getBeanName() + "'", ele, ex);
            }
            // Send registration event.
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));    // #4
        }
    }

#1 根据xmleizo,生成BeanDefinitionHolder

#2 应用NamespaceHandler#decorate扩展方法

#3 将BeanDefinition注册到spring上下文

#4 发送注册事件

这里出现了一个很重要的类BeanDefinition。

BeanDefinition是spring对bean元数据的抽象, 是配置文件中 <bean> 元素在spring容器中的内部表示类,存储了bean的元数据, 如属性PropertyValues, BeanClassName,Scope等。

BeanDefinitionHolder中持有BeanDefinition实例,以及beanName和aliases属性。

构造BeanDefinition

BeanDefinitionParserDelegat#parseBeanDefinitionElement有几个重载方法,最终都调用如下方法

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

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

    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);
        }
        AbstractBeanDefinition bd = createBeanDefinition(className, parent); // #1

        parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);    // #2
        bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));    

        parseMetaElements(ele, bd);    // #3
        parseLookupOverrideSubElements(ele, bd.getMethodOverrides());    // #4
        parseReplacedMethodSubElements(ele, bd.getMethodOverrides());    // #5

        parseConstructorArgElements(ele, bd);    // #6
        parsePropertyElements(ele, bd);    // #7
        parseQualifierElements(ele, bd);    // #8

        bd.setResource(this.readerContext.getResource());
        bd.setSource(extractSource(ele));

        return bd;
    }
    ...
}

#1 使用className和parent创建BeanDefinition

#2 处理scope等元素属性

#3 处理meta元素属性

#4 处理lookup-method元素属性

#5 处理replaced-method元素属性

#6 处理constructor-arg构造方法参数

#7 处理property元素

#8 处理qualifier元素

看一下如何处理property元素

BeanDefinitionParserDelegate#parsePropertyElements -> BeanDefinitionParserDelegate#parsePropertyElement

public void parsePropertyElement(Element ele, BeanDefinition bd) {
    String propertyName = ele.getAttribute(NAME_ATTRIBUTE);    // #1
    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);    // #2
        PropertyValue pv = new PropertyValue(propertyName, val);    // #3
        parseMetaElements(ele, pv);
        pv.setSource(extractSource(ele));
        bd.getPropertyValues().addPropertyValue(pv);    // #4
    }
    finally {
        this.parseState.pop();
    }
}

#1 解析属性名

#2 将xml配置的属性值转化为内部表示类

#3 创建PropertyValue,PropertyValue中name存放属性名,value存放了xml配置原始值。

#4 添加到BeanDefinition,BeanDefinition使用MutablePropertyValues存放所有的属性信息。

xml配置原始值并不是属性最终值,而是xml配置在spring中对应的内部表示类,如property元素的value属性会表示为TypedStringValue(类似于BeanDefinition表示<bean>元素)。

BeanDefinitionParserDelegate#parsePropertyValue

public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {
    ...

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

    if (hasRefAttribute) {    // #1
        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;
    }
    else if (hasValueAttribute) {    // #2
        TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
        valueHolder.setSource(extractSource(ele));
        return valueHolder;
    }
    else if (subElement != null) {    // #3
        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;
    }
}

#1 如果property元素存在ref属性,解析为RuntimeBeanReference

#2 如果存在value属性,解析为TypedStringValue

#3 如果存在子标签,就解析子标签。子标签和spring内部表示类对应如下

子标签为bean,解析为BeanDefinitionHolder

子标签为ref,解析为RuntimeBeanReference

子标签为idref,解析为RuntimeBeanNameReference

子标签为value,解析为TypedStringValue

子标签为null,解析为TypedStringValue

子标签为array,解析为ManagedArray

子标签为list,解析为ManagedList

子标签为set,解析为ManagedSet

子标签为map,解析为ManagedMap

子标签为props,解析为ManagedProperties

注册

回到DefaultBeanDefinitionDocumentReader#processBeanDefinition方法 #3 步骤, 看一下如何将BeanDefinition注册到spring上下文

BeanDefinitionReaderUtils#registerBeanDefinition

public static void registerBeanDefinition(
        BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
        throws BeanDefinitionStoreException {
    
    String beanName = definitionHolder.getBeanName();
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());    // #1

    String[] aliases = definitionHolder.getAliases();
    if (aliases != null) {
        for (String alias : aliases) {
            registry.registerAlias(beanName, alias);    // #2
        }
    }
}

#1 使用beanName注册bean

#2 注册别名alias

注册器registry从ReaderContext中获取,实际就是DefaultListableBeanFactory(注意上文对ReaderContext的说明)。

DefaultListableBeanFactory#registerBeanDefinition

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

    oldBeanDefinition = this.beanDefinitionMap.get(beanName);
    if (oldBeanDefinition != null) {
        ...
    }
    else {
        if (hasBeanCreationStarted()) {    // #1
            synchronized (this.beanDefinitionMap) {    // #2
                this.beanDefinitionMap.put(beanName, beanDefinition);    // #3
                List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
                updatedDefinitions.addAll(this.beanDefinitionNames);
                updatedDefinitions.add(beanName);
                this.beanDefinitionNames = updatedDefinitions;    // #4
                if (this.manualSingletonNames.contains(beanName)) {
                    Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
                    updatedSingletons.remove(beanName);
                    this.manualSingletonNames = updatedSingletons;    // #5
                }
            }
        }
        else {    // #6
            this.beanDefinitionMap.put(beanName, beanDefinition);
            this.beanDefinitionNames.add(beanName);
            this.manualSingletonNames.remove(beanName);
        }
        this.frozenBeanDefinitionNames = null;
    }

    if (oldBeanDefinition != null || containsSingleton(beanName)) {
        resetBeanDefinition(beanName);
    }
}

#1 判断spring是否进入构建bean的阶段

#2 beanDefinitionNames/manualSingletonNames不是线程安全类,需要加同步

#3 添加beanDefinition到DefaultListableBeanFactory#beanDefinitionMap

#4 通过写时复制,添加beanName到DefaultListableBeanFactory#beanDefinitionNames

#5 通过写时复制,删除manualSingletonNames的DefaultListableBeanFactory#beanDefinitionNames

#6 还在注册阶段,不需要加锁同步

最后, 看一下DefaultBeanDefinitionDocumentReader#parseBeanDefinitions方法 #2 步骤对非beans空间的xml标签的处理, BeanDefinitionParserDelegate#parseCustomElement

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
    String namespaceUri = getNamespaceURI(ele);
    NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);    // #1
    if (handler == null) {
        error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
        return null;
    }
    return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));    // #2
}

#1 通过命名空间获取特定的NamespaceHandler

#2 使用NamespaceHandler进行处理

如果您觉得本文不错,欢迎关注我的微信公众号,您的关注是我坚持的动力!

Spring源码解析 -- 读取bean元数据

原文 

https://segmentfault.com/a/1190000022490916

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » Spring源码解析 — 读取bean元数据

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址