Spring 源码解读第七弹!bean 标签的解析

Spring 源码解读继续。java

本文是 Spring 系列第八篇,若是小伙伴们还没阅读过本系列前面的文章,建议先看看,这有助于更好的理解本文。node

  1. Spring 源码解读计划
  2. Spring 源码第一篇开整!配置文件是怎么加载的?
  3. Spring 源码第二弹!XML 文件解析流程
  4. Spring 源码第三弹!EntityResolver 是个什么鬼?
  5. Spring 源码第四弹!深刻理解 BeanDefinition
  6. 手把手教你搭建 Spring 源码分析环境
  7. Spring 源码第六弹!松哥和你们聊聊容器的始祖 DefaultListableBeanFactory

1.前文回顾

不知道小伙伴们是否还记得,在前面咱们讲 Spring 文档加载的时候,涉及到以下一段源码:spring

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
        throws BeanDefinitionStoreException {
    try {
        Document doc = doLoadDocument(inputSource, resource);
        int count = registerBeanDefinitions(doc, resource);
        if (logger.isDebugEnabled()) {
            logger.debug("Loaded " + count + " bean definitions from " + resource);
        }
        return count;
    }
    catch (BeanDefinitionStoreException ex) {
        throw ex;
    }
    catch (SAXParseException ex) {
        throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
    }
    catch (SAXException ex) {
        throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                "XML document from " + resource + " is invalid", ex);
    }
    catch (ParserConfigurationException ex) {
        throw new BeanDefinitionStoreException(resource.getDescription(),
                "Parser configuration exception parsing XML from " + resource, ex);
    }
    catch (IOException ex) {
        throw new BeanDefinitionStoreException(resource.getDescription(),
                "IOException parsing XML document from " + resource, ex);
    }
    catch (Throwable ex) {
        throw new BeanDefinitionStoreException(resource.getDescription(),
                "Unexpected exception parsing XML document from " + resource, ex);
    }
}

这段代码就两个核心方法:express

  1. 首先调用 doLoadDocument 方法获取 Spring 的 XML 配置文件加载出来的 Document 文档对象,这个方法的执行流程咱们在前面已经介绍过了,这里就再也不赘述。
  2. 接下来就是调用 registerBeanDefinitions 方法,讲加载出来的文档对象进行解析,定义出相应的 BeanDefinition 对象出来。

BeanDefinition 是什么,有什么做用,松哥在以前的 Spring 源码第四弹!深刻理解 BeanDefinition 一文中已经作过介绍,这里就再也不赘述。ide

本文咱们就来看看 Document 对象是如何一步一步加载成 BeanDefinition 的。函数

2.parseDefaultElement

咱们就从 registerBeanDefinitions 方法开始看起:源码分析

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

这里经过调用 createBeanDefinitionDocumentReader 方法获取到一个 BeanDefinitionDocumentReader 的实例,具体的对象则是 DefaultBeanDefinitionDocumentReader,也就是说接下来调用 DefaultBeanDefinitionDocumentReader#registerBeanDefinitions 进行解析。继续来看该方法的定义:post

@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    this.readerContext = readerContext;
    doRegisterBeanDefinitions(doc.getDocumentElement());
}

这里又调用到了 doRegisterBeanDefinitions 方法继续完成注册:ui

protected void doRegisterBeanDefinitions(Element root) {
    // Any nested <beans> elements will cause recursion in this method. In
    // order to propagate and preserve <beans> default-* attributes correctly,
    // keep track of the current (parent) delegate, which may be null. Create
    // the new (child) delegate with a reference to the parent for fallback purposes,
    // then ultimately reset this.delegate back to its original (parent) reference.
    // this behavior emulates a stack of delegates without actually necessitating one.
    BeanDefinitionParserDelegate parent = this.delegate;
    this.delegate = createDelegate(getReaderContext(), root, parent);
    if (this.delegate.isDefaultNamespace(root)) {
        String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
        if (StringUtils.hasText(profileSpec)) {
            String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                    profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            // We cannot use Profiles.of(...) since profile expressions are not supported
            // in XML config. See SPR-12458 for details.
            if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                            "] not matching: " + getReaderContext().getResource());
                }
                return;
            }
        }
    }
    preProcessXml(root);
    parseBeanDefinitions(root, this.delegate);
    postProcessXml(root);
    this.delegate = parent;
}

这个方法流程比较简单,首先检查了一下有没有 profile 须要处理(若是有人不清楚 Spring 中的 profile,能够在公众号后台回复 spring5 获取松哥录制的免费的 Spring 入门教程)。处理完 profile 以后,接下来就是解析了,解析有一个前置处理方法 preProcessXml 和后置处理方法 postProcessXml,不过这两个方法默认都是空方法,真正的解析方法是 parseBeanDefinitions:this

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);
                }
                else {
                    delegate.parseCustomElement(ele);
                }
            }
        }
    }
    else {
        delegate.parseCustomElement(root);
    }
}

在该方法中进行节点的解析,最终会来到 parseDefaultElement 方法中。咱们一块儿来看下该方法:

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

终于来到期盼已久的 parseDefaultElement 方法中了。

在该方法中,咱们能够看到,节点一共被分为了四大类:

  • import
  • alias
  • bean
  • beans

每个节点都好理解,由于咱们在开发中可能多多少少都有用过,须要注意的是,若是是 beans 节点,又会再次调用 doRegisterBeanDefinitions 方法进行递归解析,源码上面还给了一个注释 recurse,意思就是递归。

四种类型的节点解析,咱们就从 bean 的解析看起吧,由于 beans 节点是咱们最经常使用的节点,这个搞清楚了,另外三个节点就能够触类旁通了。

咱们来看 processBeanDefinition 方法:

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

在这段代码中,首先调用代理类 BeanDefinitionParserDelegate 对元素进行解析,解析的结果会保存在 bdHolder 中,也就是 bean 节点中配置的元素 class、id、name 等属性,在通过这一步的解析以后,都会保存到 bdHolder 中。

若是 bdHolder 不为空,那么接下来对子节点的属性继续解析,同时对 bdHolder 进行注册,最终发出事件,通知这个 bean 节点已经加载完了。

如此看来,整个解析的核心过程应该在 delegate.parseBeanDefinitionElement(ele) 方法中,追踪该方法的执行,咱们最终来到这里:

@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
    String id = ele.getAttribute(ID_ATTRIBUTE);
    String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
    List<String> aliases = new ArrayList<>();
    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.isTraceEnabled()) {
            logger.trace("No XML 'id' specified - using '" + beanName +
                    "' as bean name and " + aliases + " as aliases");
        }
    }
    if (containingBean == null) {
        checkNameUniqueness(beanName, aliases, ele);
    }
    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.isTraceEnabled()) {
                    logger.trace("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;
}

这个方法中所做的事情咱们能够大体分为 5 个步骤:

  1. 提取出 id 和 name 属性值。
  2. 检查 beanName 是否惟一。
  3. 对节点作进一步的解析,解析出 beanDefinition 对象,真是的类型是 GenericBeanDefinition。
  4. 若是 beanName 属性没有值,则使用默认的规则生成 beanName(默认规则是类名全路径)。
  5. 最终将获取到的信息封装成一个 BeanDefinitionHolder 返回。

在这一层面主要完成了对 id 和 name 的处理,若是用户没有给 bean 定义名称的话,则生成一个默认的名称,至于其余属性的解析,则主要是在 parseBeanDefinitionElement 方法中完成的。

@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
        Element ele, String beanName, @Nullable BeanDefinition containingBean) {
    this.parseState.push(new BeanEntry(beanName));
    String className = null;
    if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
        className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
    }
    String parent = null;
    if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
        parent = ele.getAttribute(PARENT_ATTRIBUTE);
    }
    try {
        AbstractBeanDefinition bd = createBeanDefinition(className, parent);
        parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
        bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
        parseMetaElements(ele, bd);
        parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
        parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
        parseConstructorArgElements(ele, bd);
        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;
}
  1. 首先解析出 className 属性。
  2. 解析出 parent 属性。
  3. 调用 createBeanDefinition 方法建立出用于保存对象的 BeanDefinition,既 GenericBeanDefinition。
  4. parseBeanDefinitionAttributes 用来解析出各类各样的节点属性。
  5. parseMetaElements 用来解析 Meta 数据。
  6. parseLookupOverrideSubElements 解析 lookup-method 属性。
  7. parseReplacedMethodSubElements 解析 replace-method 属性。
  8. parseConstructorArgElements 解析构造函数参数。
  9. parsePropertyElements 解析 property 子元素。
  10. parseQualifierElements 解析 qualifier 子元素。
  11. 最终返回 bd。

能够看到,bean 节点中全部的属性都解析了,有的是咱们平常常见的属性,有的是咱们不常见的甚至历来都没见到过的,不管哪一种状况,如今所有都解析了。解析完成后,将得到的 GenericBeanDefinition 返回。

3. 常规属性解析

这里有一些属性的解析可能比较冷门,这个我一会说,还有一些比较常规,例如 parseBeanDefinitionAttributes 方法用来解析各类各样的节点属性,这些节点属性可能你们都比较熟悉,咱们一块儿来看下:

public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
        @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
    if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
        error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
    }
    else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
        bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
    }
    else if (containingBean != null) {
        // Take default from containing bean in case of an inner bean definition.
        bd.setScope(containingBean.getScope());
    }
    if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
        bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
    }
    String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
    if (isDefaultValue(lazyInit)) {
        lazyInit = this.defaults.getLazyInit();
    }
    bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
    String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
    bd.setAutowireMode(getAutowireMode(autowire));
    if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
        String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
        bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
    }
    String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
    if (isDefaultValue(autowireCandidate)) {
        String candidatePattern = this.defaults.getAutowireCandidates();
        if (candidatePattern != null) {
            String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
            bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
        }
    }
    else {
        bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
    }
    if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
        bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
    }
    if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
        String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
        bd.setInitMethodName(initMethodName);
    }
    else if (this.defaults.getInitMethod() != null) {
        bd.setInitMethodName(this.defaults.getInitMethod());
        bd.setEnforceInitMethod(false);
    }
    if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
        String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
        bd.setDestroyMethodName(destroyMethodName);
    }
    else if (this.defaults.getDestroyMethod() != null) {
        bd.setDestroyMethodName(this.defaults.getDestroyMethod());
        bd.setEnforceDestroyMethod(false);
    }
    if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
        bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
    }
    if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
        bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
    }
    return bd;
}

能够看到,这里解析的节点属性,从上往下,依次是:

  1. 解析 singleton 属性(该属性已废弃,使用 scope 替代)。
  2. 解析 scope 属性,若是未指定 scope 属性,可是存在 containingBean,则使用 containingBean 的 scope 属性值。
  3. 解析 abstract 属性。
  4. 解析 lazy-init 属性。
  5. 解析 autowire 属性。
  6. 解析 depends-on 属性。
  7. 解析 autowire-candidate 属性。
  8. 解析 primary 属性。
  9. 解析 init-method 属性。
  10. 解析 destroy-method 属性。
  11. 解析 factory-method 属性。
  12. 解析 factory-bean 属性。

这些属性做用你们都比较熟悉。由于平常用的多一些。

前面提到的解析中,lookup-method、replace-method、以及 qualifier 等属性可能你们平常都不多用到,甚至没有据说过,若是用都没用过,那源码确定很差理解,因此接下来松哥会录制一个视频,来和你们讲一讲这些冷门属性的使用,而后咱们再继续深刻解析这里的 parseMetaElements、parseLookupOverrideSubElements 等方法。

4. Bean 的生成

有了 BeanDefinitionHolder 以后,接下来 Bean 的生成就很容易了。

你们回顾以下两篇文章来理解有了 BeanDefinition 以后,如何转化为具体的 Bean:

好啦,今天的文章就先说这么多~