Spring源码分析4---IOC加载过程补充总结

原文出自:http://cmsblogs.com

IOC 之 获取验证模型

DTD 与 XSD 的区别

DTD(Document Type Definition),即文档类型定义,为 XML 文件的验证机制,属于 XML 文件中组成的一部分。DTD 是一种保证 XML 文档格式正确的有效验证方式,它定义了相关 XML 文档的元素、属性、排列方式、元素的内容类型以及元素的层次结构。其实 DTD 就相当于 XML 中的 “词汇”和“语法”,我们可以通过比较 XML 文件和 DTD 文件 来看文档是否符合规范,元素和标签使用是否正确。

要在 Spring 中使用 DTD,需要在 Spring XML 文件头部声明:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC  "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd">

DTD 在一定的阶段推动了 XML 的发展,但是它本身存在着一些缺陷:

  1. 它没有使用 XML 格式,而是自己定义了一套格式,相对解析器的重用性较差;而且 DTD 的构建和访问没有标准的编程接口,因而解析器很难简单的解析 DTD 文档。
  2. DTD 对元素的类型限制较少;同时其他的约束力也叫弱。
  3. DTD 扩展能力较差。
  4. 基于正则表达式的 DTD 文档的描述能力有限。

针对 DTD 的缺陷,W3C 在 2001 年推出 XSD。XSD(XML Schemas Definition)即 XML Schema 语言。XML Schema 本身就是一个 XML文档,使用的是 XML 语法,因此可以很方便的解析 XSD 文档。相对于 DTD,XSD 具有如下优势:

  • XML Schema基于XML,没有专门的语法
  • XML Schema可以象其他XML文件一样解析和处理
  • XML Schema比DTD提供了更丰富的数据类型.
  • XML Schema提供可扩充的数据模型。
  • XML Schema支持综合命名空间
  • XML Schema支持属性组。

getValidationModeForResource() 分析

   protected int getValidationModeForResource(Resource resource) {
        // 获取指定的验证模式
        int validationModeToUse = getValidationMode();
        // 如果手动指定,则直接返回
        if (validationModeToUse != VALIDATION_AUTO) {
            return validationModeToUse;
        }
        // 通过程序检测
        int detectedMode = detectValidationMode(resource);
        if (detectedMode != VALIDATION_AUTO) {
            return detectedMode;
        }

        // 出现异常,返回 XSD
        return VALIDATION_XSD;
    }

如果指定了 XML 文件的的验证模式(调用XmlBeanDefinitionReader.setValidating(boolean validating))则直接返回指定的验证模式,否则调用 detectValidationMode() 获取相应的验证模式,如下:

  protected int detectValidationMode(Resource resource) {
        if (resource.isOpen()) {
            throw new BeanDefinitionStoreException(
                    "Passed-in Resource [" + resource + "] contains an open stream: " +
                    "cannot determine validation mode automatically. Either pass in a Resource " +
                    "that is able to create fresh streams, or explicitly specify the validationMode " +
                    "on your XmlBeanDefinitionReader instance.");
        }

        InputStream inputStream;
        try {
            inputStream = resource.getInputStream();
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(
                    "Unable to determine validation mode for [" + resource + "]: cannot open InputStream. " +
                    "Did you attempt to load directly from a SAX InputSource without specifying the " +
                    "validationMode on your XmlBeanDefinitionReader instance?", ex);
        }

        try {
          // 核心方法
            return this.validationModeDetector.detectValidationMode(inputStream);
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException("Unable to determine validation mode for [" +
                    resource + "]: an error occurred whilst reading from the InputStream.", ex);
        }
    }

前面一大堆的代码,核心在于 this.validationModeDetector.detectValidationMode(inputStream),validationModeDetector 定义为 XmlValidationModeDetector,所以验证模式的获取委托给 XmlValidationModeDetector 的 detectValidationMode() 方法。

 public int detectValidationMode(InputStream inputStream) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        try {
            boolean isDtdValidated = false;
            String content;
            // 一行一行读取 xml 文件的内容
            while ((content = reader.readLine()) != null) {
                content = consumeCommentTokens(content);
                if (this.inComment || !StringUtils.hasText(content)) {
                    continue;
                }
                // 包含 DOCTYPE 为 DTD 模式
                if (hasDoctype(content)) {
                    isDtdValidated = true;
                    break;
                }
                // 读取 < 开始符号,验证模式一定会在 < 符号之前
                if (hasOpeningTag(content)) {
                    // End of meaningful data...
                    break;
                }
            }
        // 为 true 返回 DTD,否则返回 XSD
            return (isDtdValidated ? VALIDATION_DTD : VALIDATION_XSD);
        }
        catch (CharConversionException ex) {
            // 出现异常,为 XSD
            return VALIDATION_AUTO;
        }
        finally {
            reader.close();
        }
    }

从代码中看,主要是通过读取 XML 文件的内容,判断内容中是否包含有 DOCTYPE ,如果是 则为 DTD,否则为 XSD,当然只会读取到 第一个 “<” 处,因为 验证模式一定会在第一个 “<” 之前。如果当中出现了 CharConversionException 异常,则为 XSD模式。

IOC 之解析Bean:解析 import 标签

Spring 中有两种解析 Bean 的方式。如果根节点或者子节点采用默认命名空间的话,则调用 parseDefaultElement() 进行默认标签解析,否则调用 delegate.parseCustomElement() 方法进行自定义解析。所以以下博客就这两个方法进行详细分析说明,先从默认标签解析过程开始,源码如下:

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 标签的解析
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            processBeanDefinition(ele, delegate);
        }
        // 对 beans 标签的解析
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // recurse
            doRegisterBeanDefinitions(ele);
        }
    }

import 标签的处理

经历过 Spring 配置文件的小伙伴都知道,如果工程比较大,配置文件的维护会让人觉得恐怖,文件太多了,想象将所有的配置都放在一个 spring.xml 配置文件中,哪种后怕感是不是很明显?所有针对这种情况 Spring 提供了一个分模块的思路,利用 import 标签,例如我们可以构造一个这样的 spring.xml。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <import resource="spring-student.xml"/>
    <import resource="spring-student-dtd.xml"/>
</beans>

spring.xml 配置文件中使用 import 标签的方式导入其他模块的配置文件,如果有配置需要修改直接修改相应配置文件即可,若有新的模块需要引入直接增加 import 即可,这样大大简化了配置后期维护的复杂度,同时也易于管理。

Spring 利用 importBeanDefinitionResource() 方法完成对 import 标签的解析。

 protected void importBeanDefinitionResource(Element ele) {
        // 获取 resource 的属性值 
        String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
        // 为空,直接退出
        if (!StringUtils.hasText(location)) {
            getReaderContext().error("Resource location must not be empty", ele);
            return;
        }

        // 解析系统属性,格式如 :"${user.dir}"
        location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);

        Set<Resource> actualResources = new LinkedHashSet<>(4);

        // 判断 location 是相对路径还是绝对路径
        boolean absoluteLocation = false;
        try {
            absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
        }
        catch (URISyntaxException ex) {
            // cannot convert to an URI, considering the location relative
            // unless it is the well-known Spring prefix "classpath*:"
        }

        // 绝对路径
        if (absoluteLocation) {
            try {
                // 直接根据地质加载相应的配置文件
                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 {
            // 相对路径则根据相应的地质计算出绝对路径地址
            try {
                int importCount;
                Resource relativeResource = getReaderContext().getResource().createRelative(location);
                if (relativeResource.exists()) {
                    importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
                    actualResources.add(relativeResource);
                }
                else {
                    String baseLocation = getReaderContext().getResource().getURL().toString();
                    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[0]);
        getReaderConte

解析 import 过程较为清晰,整个过程如下:

  1. 获取 source 属性的值,该值表示资源的路径
  2. 解析路径中的系统属性,如”${user.dir}”
  3. 判断资源路径 location 是绝对路径还是相对路径
  4. 如果是绝对路径,则调递归调用 Bean 的解析过程,进行另一次的解析
  5. 如果是相对路径,则先计算出绝对路径得到 Resource,然后进行解析
  6. 通知监听器,完成解析

判断路径

方法通过以下方法来判断 location 是为相对路径还是绝对路径:

absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();

判断绝对路径的规则如下:

  1. 以 classpath*: 或者 classpath: 开头为绝对路径
  2. 能够通过该 location 构建出 java.net.URL为绝对路径
  3. 根据 location 构造 java.net.URI 判断调用 isAbsolute() 判断是否为绝对路径

绝对路径

如果 location 为绝对路径则调用 loadBeanDefinitions(),该方法在 AbstractBeanDefinitionReader 中定义。

  public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
        ResourceLoader resourceLoader = getResourceLoader();
        if (resourceLoader == null) {
            throw new BeanDefinitionStoreException(
                    "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
        }

        if (resourceLoader instanceof ResourcePatternResolver) {
            // Resource pattern matching available.
            try {
                Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
                int loadCount = loadBeanDefinitions(resources);
                if (actualResources != null) {
                    for (Resource resource : resources) {
                        actualResources.add(resource);
                    }
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
                }
                return loadCount;
            }
            catch (IOException ex) {
                throw new BeanDefinitionStoreException(
                        "Could not resolve bean definition resource pattern [" + location + "]", ex);
            }
        }
        else {
            // Can only load single resources by absolute URL.
            Resource resource = resourceLoader.getResource(location);
            int loadCount = loadBeanDefinitions(resource);
            if (actualResources != null) {
                actualResources.add(resource);
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
            }
            return loadCount;
        }
    }

整个逻辑比较简单,首先获取 ResourceLoader,然后根据不同的 ResourceLoader 执行不同的逻辑,主要是可能存在多个 Resource,但是最终都会回归到 XmlBeanDefinitionReader.loadBeanDefinitions() ,所以这是一个递归的过程。

相对路径

如果是相对路径则会根据相应的 Resource 计算出相应的绝对路径,然后根据该路径构造一个 Resource,若该 Resource 存在,则调用 XmlBeanDefinitionReader.loadBeanDefinitions() 进行 BeanDefinition 加载,否则构造一个绝对 location ,调用 AbstractBeanDefinitionReader.loadBeanDefinitions() 方法,与绝对路径过程一样。

至此,import 标签解析完毕,整个过程比较清晰明了:获取 source 属性值,得到正确的资源路径,然后调用 loadBeanDefinitions() 方法进行递归的 BeanDefinition 加载

IOC 之解析 bean 标签:开启解析进程

import 标签解析完毕了,再看 Spring 中最复杂也是最重要的标签 bean 标签的解析过程。

在方法 parseDefaultElement() 中,如果遇到标签 为 bean 则调用 processBeanDefinition() 方法进行 bean 标签解析,如下:

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

整个过程分为四个步骤

  1. 调用 BeanDefinitionParserDelegate.parseBeanDefinitionElement() 进行元素解析,解析过程中如果失败,返回 null,错误由 ProblemReporter 处理。如果解析成功则返回 BeanDefinitionHolder 实例 bdHolder。BeanDefinitionHolder 为持有 name 和 alias 的 BeanDefinition。
  2. 若实例 bdHolder 不为空,则调用 BeanDefinitionParserDelegate.decorateBeanDefinitionIfRequired() 进行自定义标签处理
  3. 解析完成后,则调用 BeanDefinitionReaderUtils.registerBeanDefinition() 对 bdHolder 进行注册
  4. 发出响应事件,通知相关的监听器,完成 Bean 标签解析

先看方法 parseBeanDefinitionElement(),如下:

 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
        // 解析 ID 属性
        String id = ele.getAttribute(ID_ATTRIBUTE);
        // 解析 name 属性
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

        // 分割 name 属性
        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.isDebugEnabled()) {
                logger.debug("No XML 'id' specified - using '" + beanName +
                        "' as bean name and " + aliases + " as aliases");
            }
        }

        // 检查 name 的唯一性
        if (containingBean == null) {
            checkNameUniqueness(beanName, aliases, ele);
        }

        // 解析 属性,构造 AbstractBeanDefinition
        AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
        if (beanDefinition != null) {
            // 如果 beanName 不存在,则根据条件构造一个 beanName
            if (!StringUtils.hasText(beanName)) {
                try {
                    if (containingBean != null) {
                        beanName = BeanDefinitionReaderUtils.generateBeanName(
                                beanDefinition, this.readerContext.getRegistry(), true);
                    }
                    else {
                        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
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        }

        return null;
    }

这个方法还没有对 Bean 标签进行解析,只是在解析动作之前做了一些功能架构,主要的工作有:

  1. 解析 id、name 属性,确定 alias 集合,检测 beanName 是否唯一
  2. 调用方法 parseBeanDefinitionElement() 对属性进行解析并封装成 GenericBeanDefinition 实例 beanDefinition
  3. 根据所获取的信息(beanName、aliases、beanDefinition)构造 BeanDefinitionHolder 实例对象并返回。

这里有必要说下 beanName 的命名规则:如果 id 不为空,则 beanName = id;如果 id 为空,但是 alias 不空,则 beanName 为 alias 的第一个元素,如果两者都为空,则根据默认规则来设置 beanName。

上面三个步骤第二个步骤为核心方法,它主要承担解析 Bean 标签中所有的属性值。如下:

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

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

        String className = null;
        // 解析 class 属性
        if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
            className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
        }
        String parent = null;

        // 解析 parent 属性
        if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
            parent = ele.getAttribute(PARENT_ATTRIBUTE);
        }

        try {

            // 创建用于承载属性的 GenericBeanDefinition 实例
            AbstractBeanDefinition bd = createBeanDefinition(className, parent);

            // 解析默认 bean 的各种属性
            parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);

            // 提取 description
            bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

            // 解析元数据
            parseMetaElements(ele, bd);

            // 解析 lookup-method 属性
            parseLookupOverrideSubElements(ele, bd.getMethodOverrides());

            // 解析 replaced-method 属性
            parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

            // 解析构造函数参数
            parseConstructorArgElements(ele, bd);

            // 解析 property 子元素
            parsePropertyElements(ele, bd);

            // 解析 qualifier 子元素
            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;
    }

IOC 之解析 bean 标签:BeanDefinition

前面历经千辛万苦终于到达解析 bean 标签步骤来了,解析 bean 标签的过程其实就是构造一个 BeanDefinition 对象的过程, 元素标签拥有的配置属性,BeanDefinition 均提供了相应的属性,与之一一对应。所以我们有必要对 BeanDefinition 有一个整体的认识。

BeanDefinition

BeanDefinition 是一个接口,它描述了一个 Bean 实例,包括属性值、构造方法值和继承自它的类的更多信息。它继承 AttributeAccessor 和 BeanMetadataElement 接口。两个接口定义如下:

AttributeAccessor :定义了与其它对象的(元数据)进行连接和访问的约定,即对属性的修改,包括获取、设置、删除。
BeanMetadataElement:Bean 元对象持有的配置元素可以通过getSource() 方法来获取。
BeanDefinition 整个结构如下图:
在这里插入图片描述
我们常用的三个实现类有:ChildBeanDefinition、GenericBeanDefinition、RootBeanDefinition,三者都继承 AbstractBeanDefinition。如果配置文件中定义了父 和 子 ,则父 用 RootBeanDefinition表示,子 用 ChildBeanDefinition 表示,而没有父 的就使用RootBeanDefinition 表示。GenericBeanDefinition 为一站式服务类。AbstractBeanDefinition对三个子类共同的类信息进行抽象。

解析 Bean 标签

在 BeanDefinitionParserDelegate.parseBeanDefinitionElement() 中完成 Bean 的解析,返回的是一个已经完成对 标签解析的 BeanDefinition 实例。在该方法内部,首先调用 createBeanDefinition() 方法创建一个用于承载属性的 GenericBeanDefinition 实例,如下:

 protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
            throws ClassNotFoundException {
        return BeanDefinitionReaderUtils.createBeanDefinition(
                parentName, className, this.readerContext.getBeanClassLoader());
    }

委托 BeanDefinitionReaderUtils 创建,如下:

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

        GenericBeanDefinition bd = new GenericBeanDefinition();
        bd.setParentName(parentName);
        if (className != null) {
            if (classLoader != null) {
                bd.setBeanClass(ClassUtils.forName(className, classLoader));
            }
            else {
                bd.setBeanClassName(className);
            }
        }
        return bd;
    }

该方法主要是设置 parentName 、className、classLoader。

创建完 GenericBeanDefinition 实例后,再调用 parseBeanDefinitionAttributes() ,该方法将创建好的 GenericBeanDefinition 实例当做参数,对 Bean 标签的所有属性进行解析,如下:

 public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
                                                                @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
        // 解析 scope 标签
        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());
        }

        // 解析 abstract 标签
        if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
            bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
        }

        // 解析 lazy-init 标签
        String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
        if (DEFAULT_VALUE.equals(lazyInit)) {
            lazyInit = this.defaults.getLazyInit();
        }
        bd.setLazyInit(TRUE_VALUE.equals(lazyInit));

        // 解析 autowire 标签
        String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
        bd.setAutowireMode(getAutowireMode(autowire));

        // 解析 depends-on 标签
        if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
            String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
            bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
        }

        // 解析 autowire-candidate 标签
        String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
        if ("".equals(autowireCandidate) || DEFAULT_VALUE.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_VALUE.equals(autowireCandidate));
        }

        // 解析 primay 标签
        if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
            bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
        }

        // 解析 init-method 标签
        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);
        }

        // 解析 destroy-mothod 标签
        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);
        }

        // 解析 factory-method 标签
        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;
    }

从上面代码我们可以清晰地看到对 Bean 标签属性的解析,这些属性我们在工作中都或多或少用到过。

完成 Bean 标签基本属性解析后,会依次调用 parseMetaElements()、parseLookupOverrideSubElements()、parseReplacedMethodSubElements() 对子元素 meta、lookup-method、replace-method 完成解析。

IOC 之解析 bean 标签:meta、lookup-method、replace-method

三个子元素的作用如下:

  • meta:元数据。
  • lookup-method:Spring 动态改变 bean 里方法的实现。方法执行返回的对象,使用 Spring 内原有的这类对象替换,通过改变方法返回值来动态改变方法。内部实现为使用 cglib 方法,重新生成子类,重写配置的方法和返回对象,达到动态改变的效果。
  • replace-method:Spring 动态改变 bean 里方法的实现。需要改变的方法,使用 Spring 内原有其他类(需要继承接口org.springframework.beans.factory.support.MethodReplacer)的逻辑,替换这个方法。通过改变方法执行逻辑来动态改变方法。

meta 子元素

meta :元数据。当需要使用里面的信息时可以通过key获取

meta 所声明的 key 并不会在 Bean 中体现,只是一个额外的声明,当我们需要使用里面的信息时,通过 BeanDefinition 的 getAttribute() 获取。该子元素的解析过程如下:

 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)) {
                Element metaElement = (Element) node;
                String key = metaElement.getAttribute(KEY_ATTRIBUTE);
                String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
                BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
                attribute.setSource(extractSource(metaElement));
                attributeAccessor.addMetadataAttribute(attribute);
            }
        }
    }

解析过程较为简单,获取相应的 key – value 构建 BeanMetadataAttribute 对象,然后通过 addMetadataAttribute() 加入到 AbstractBeanDefinition 中。 “ 如下:

 public void addMetadataAttribute(BeanMetadataAttribute attribute) {
        super.setAttribute(attribute.getName(), attribute);
    }

委托 AttributeAccessorSupport 实现,如下:

   public void setAttribute(String name, @Nullable Object value) {
        Assert.notNull(name, "Name must not be null");
        if (value != null) {
            this.attributes.put(name, value);
        }
        else {
            removeAttribute(name);
        }
    }

AttributeAccessorSupport 是接口 AttributeAccessor 的实现者。 AttributeAccessor 接口定义了与其他对象的元数据进行连接和访问的约定,可以通过该接口对属性进行获取、设置、删除操作。

设置元数据后,则可以通过 getAttribute() 获取,如下:

 public Object getAttribute(String name) {
        BeanMetadataAttribute attribute = (BeanMetadataAttribute) super.getAttribute(name);
        return (attribute != null ? attribute.getValue() : null);
    }

lookup-method 子元素

lookup-method :获取器注入,是把一个方法声明为返回某种类型的 bean 但实际要返回的 bean 是在配置文件里面配置的。该方法可以用于设计一些可插拔的功能上,解除程序依赖。

直接上例子:

public interface Car {

    void display();
}

public class Bmw implements Car{
    @Override
    public void display() {
        System.out.println("我是 BMW");
    }
}

public class Hongqi implements Car{
    @Override
    public void display() {
        System.out.println("我是 hongqi");
    }
}

public abstract class Display {


    public void display(){
        getCar().display();
    }

    public abstract Car getCar();
}

   public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");

        Display display = (Display) context.getBean("display");
        display.display();
    }
}

配置内容如下:

<bean id="display" class="org.springframework.core.test1.Display">
        <lookup-method name="getCar" bean="hongqi"/>
    </bean>

运行结果为:

我是 hongqi

看了这个示例,我们初步了解了 looku-method 子元素提供的功能了,其解析过程如下:

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)) {
                Element ele = (Element) node;
                String methodName = ele.getAttribute(NAME_ATTRIBUTE);
                String beanRef = ele.getAttribute(BEAN_ELEMENT);
                LookupOverride override = new LookupOverride(methodName, beanRef);
                override.setSource(extractSource(ele));
                overrides.addOverride(override);
            }
        }
    }

解析过程和 meta 子元素没有多大区别,同样是解析 methodName、beanRef 构造一个 LookupOverride 对象,然后覆盖即可。在实例化 Bean 的时候,再详细阐述具体的实现过程,这里仅仅只是一个标记作用。

replace-method 子元素

replaced-method :可以在运行时调用新的方法替换现有的方法,还能动态的更新原有方法的逻辑

该标签使用方法和 lookup-method 标签差不多,只不过替代方法的类需要实现 MethodReplacer 接口。如下:

public class Method {
    public void display(){
        System.out.println("我是原始方法");
    }
}

public class MethodReplace implements MethodReplacer {

    @Override
    public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
        System.out.println("我是替换方法");

        return null;
    }
}

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");

        Method method = (Method) context.getBean("method");
        method.display();
    }

如果 spring.xml 文件如下:

 <bean id="methodReplace" class="org.springframework.core.test1.MethodReplace"/>

    <bean id="method" class="org.springframework.core.test1.Method"/>

则运行结果为:

我是原始方法

增加 replaced-method 子元素:

   <bean id="methodReplace" class="org.springframework.core.test1.MethodReplace"/>

    <bean id="method" class="org.springframework.core.test1.Method">
        <replaced-method name="display" replacer="methodReplace"/>
    </bean>

运行结果为:

我是替换方法

上面代码演示了 replaced-method 子元素的用法,下面再看看该子元素的解析过程。

  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)) {
                Element replacedMethodEle = (Element) node;
                String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE);
                String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE);
                ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);
                // Look for arg-type match elements.
                List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);
                for (Element argTypeEle : argTypeEles) {
                    String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE);
                    match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));
                    if (StringUtils.hasText(match)) {
                        replaceOverride.addTypeIdentifier(match);
                    }
                }
                replaceOverride.setSource(extractSource(replacedMethodEle));
                overrides.addOverride(replaceOverride);
            }
        }
    }

该子元素和 lookup-method 资源的解析过程差不多,同样是提取 name 和 replacer 属性构建 ReplaceOverride 对象,然后记录到 AbstractBeanDefinition 中的 methodOverrides 属性中。

IOC 之解析 bean 标签:constructor-arg、property 子元素

constructor-arg 子元素

举个小栗子:

public class StudentService {
    private String name;

    private Integer age;

    private BookService bookService;

    StudentService(String name, Integer age, BookService bookService){
        this.name = name;
        this.age = age;
        this.bookService = bookService;
    }
}

    <bean id="bookService" class="org.springframework.core.service.BookService"/>

    <bean id="studentService" class="org.springframework.core.service.StudentService">
        <constructor-arg index="0" value="chenssy"/>
        <constructor-arg name="age" value="100"/>
        <constructor-arg name="bookService" ref="bookService"/>
    </bean>

StudentService 定义一个构造函数,配置文件中使用 constructor-arg 元素对其配置,该元素可以实现对 StudentService 自动寻找对应的构造函数,并在初始化的时候将值当做参数进行设置。parseConstructorArgElements() 方法完成 constructor-arg 子元素的解析。

 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_ELEMENT)) {
                parseConstructorArgElement((Element) node, bd);
            }
        }
    }

遍历所有子元素,如果为 constructor-arg 则调用 parseConstructorArgElement() 进行解析。

 public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
        // 提取 index、type、name 属性值
        String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
        String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

        // 如果有index
        if (StringUtils.hasLength(indexAttr)) {
            try {
                int index = Integer.parseInt(indexAttr);
                if (index < 0) {
                    error("'index' cannot be lower than 0", ele);
                }
                else {
                    try {
                        // 构造一个 ConstructorArgumentEntry 并将其加入到 ParseState 中
                        this.parseState.push(new ConstructorArgumentEntry(index));

                        // 解析 ele 对应属性元素
                        Object value = parsePropertyValue(ele, bd, null);

                        // 根据解析的属性元素构造一个 valueHolder 对象
                        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 {
                            // 加入到 indexedArgumentValues 中国
                            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 {
            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();
            }
        }
    }

首先获取 index、type、name 三个属性值,然后根据是否存在 index 来区分。其实两者逻辑都差不多,总共分为如下几个步骤(以有 index 为例):

  1. 构造 ConstructorArgumentEntry 对象并将其加入到 ParseState 队列中。2. ConstructorArgumentEntry 表示构造函数的参数。
  2. 调用 parsePropertyValue() 解析 constructor-arg 子元素,返回结果值
  3. 根据解析的结果值构造 ConstructorArgumentValues.ValueHolder 实例对象
  4. 将 type、name 封装到 ConstructorArgumentValues.ValueHolder 中,然后将 ValueHolder 实例对象添加到 indexedArgumentValues 中。

无 index 的处理逻辑差不多,只有几点不同:构造 ConstructorArgumentEntry 对象时是调用无参构造函数;最后是将 ValueHolder 实例添加到 genericArgumentValues 中。

parsePropertyValue() 对子元素进一步解析。

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

        NodeList nl = ele.getChildNodes();
        Element subElement = null;
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            // meta 、description 不处理
            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;
                }
            }
        }

        // 解析 ref 元素
        boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
        // 解析 value 元素
        boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);

        // constructor-arg 子元素有两种情况不存在
        // 1. 即存在 ref 又存在 value
        // 2. 存在 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) {
            // 获取 ref 属性值
            String refName = ele.getAttribute(REF_ATTRIBUTE);
            if (!StringUtils.hasText(refName)) {
                error(elementName + " contains empty 'ref' attribute", ele);
            }
            // 将 ref 属性值构造为 RuntimeBeanReference 实例对象
            RuntimeBeanReference ref = new RuntimeBeanReference(refName);
            ref.setSource(extractSource(ele));
            return ref;
        }
        else if (hasValueAttribute) {
            // 解析 value 属性值,构造 TypedStringValue 实例对象
            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;
        }
    }
  1. 提取 constructor-arg 子元素的 ref 和 value 的属性值,对其进行判断,以下两种情况是不允许存在的
    a.ref 和 value 属性同时存在
    b.存在 ref 或者 value 且又有子元素
  2. 若存在 ref 属性,则获取其值并将其封装进 RuntimeBeanReference 实例对象中
  3. 若存在 value 属性,则获取其值并将其封装进 TypedStringValue 实例对象中
  4. 如果子元素不为空,则调用 parsePropertySubElement() 进行子元素进一步处理

对于 constructor-arg 子元素的嵌套子元素,需要调用 parsePropertySubElement() 进一步处理。

 public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) {
        return parsePropertySubElement(ele, bd, null);
    }

    public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
        if (!isDefaultNamespace(ele)) {
            return parseNestedCustomElement(ele, bd);
        }
        else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
            BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
            if (nestedBd != null) {
                nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
            }
            return nestedBd;
        }
        else if (nodeNameEquals(ele, REF_ELEMENT)) {
            // A generic reference to any name of any bean.
            String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
            boolean toParent = false;
            if (!StringUtils.hasLength(refName)) {
                // A reference to the id of another bean in a parent context.
                refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
                toParent = true;
                if (!StringUtils.hasLength(refName)) {
                    error("'bean' 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_ELEMENT)) {
            return parseIdRefElement(ele);
        }
        else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
            return parseValueElement(ele, defaultValueType);
        }
        else if (nodeNameEquals(ele, NULL_ELEMENT)) {
            // It's a distinguished null value. Let's wrap it in a TypedStringValue
            // object in order to preserve the source location.
            TypedStringValue nullHolder = new TypedStringValue(null);
            nullHolder.setSource(extractSource(ele));
            return nullHolder;
        }
        else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
            return parseArrayElement(ele, bd);
        }
        else if (nodeNameEquals(ele, LIST_ELEMENT)) {
            return parseListElement(ele, bd);
        }
        else if (nodeNameEquals(ele, SET_ELEMENT)) {
            return parseSetElement(ele, bd);
        }
        else if (nodeNameEquals(ele, MAP_ELEMENT)) {
            return parseMapElement(ele, bd);
        }
        else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
            return parsePropsElement(ele);
        }
        else {
            error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
            return null;
        }
    }

上面对各个子类进行分类处理,详细情况如果各位有兴趣可以移步源码进行深一步的探究。

property 子元素

我们一般使用如下方式来使用 property 子元素。

   <bean id="studentService" class="org.springframework.core.service.StudentService">
        <property name="name" value="chenssy"/>
        <property name="age" value="18"/>
    </bean>

对于 property 子元素的解析,Spring 调用 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);
            if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
                parsePropertyElement((Element) node, bd);
            }
        }
    }

和 constructor-arg 子元素差不多,同样是提取所有的 property 的子元素,然后调用 parsePropertyElement() 进行分析。

 public void parsePropertyElement(Element ele, BeanDefinition bd) {
        // 获取 name 属性
        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 {
            // 如果存在相同的 name
            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));
            // 添加到 MutablePropertyValues 中
            bd.getPropertyValues().addPropertyValue(pv);
        }
        finally {
            this.parseState.pop();
        }
    }

与解析 constructor-arg 子元素步骤差不多。调用 parsePropertyValue() 解析子元素属性值,然后根据该值构造 PropertyValue 实例对象并将其添加到 BeanDefinition 中的 MutablePropertyValues 中。

IOC 之注册解析的 BeanDefinition

注册 BeanDefinition 由 BeanDefinitionReaderUtils.registerBeanDefinition() 完成。如下:

 public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {

        // 注册 beanName
        String beanName = definitionHolder.getBeanName();
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

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

首先通过 beanName 注册 BeanDefinition ,然后再注册别名 alias。BeanDefinition 的注册由接口 BeanDefinitionRegistry 定义。

通过 beanName 注册

BeanDefinitionRegistry.registerBeanDefinition() 实现通过 beanName 注册 BeanDefinition,如下:

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

        // 校验 beanName 与 beanDefinition
        Assert.hasText(beanName, "Bean name must not be empty");
        Assert.notNull(beanDefinition, "BeanDefinition must not be null");

        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                // 校验 BeanDefinition
                // 这是注册前的最后一次校验了,主要是对属性 methodOverrides 进行校验
                ((AbstractBeanDefinition) beanDefinition).validate();
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Validation of bean definition failed", ex);
            }
        }

        BeanDefinition oldBeanDefinition;

        // 从缓存中获取指定 beanName 的 BeanDefinition
        oldBeanDefinition = this.beanDefinitionMap.get(beanName);
        /**
         * 如果存在
         */
        if (oldBeanDefinition != null) {
            // 如果存在但是不允许覆盖,抛出异常
            if (!isAllowBeanDefinitionOverriding()) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
                                "': There is already [" + oldBeanDefinition + "] bound.");
            }
            //
            else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
                // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
                            "' with a framework-generated bean definition: replacing [" +
                            oldBeanDefinition + "] with [" + beanDefinition + "]");
                }
            }
            // 覆盖 beanDefinition 与 被覆盖的 beanDefinition 不是同类
            else if (!beanDefinition.equals(oldBeanDefinition)) {
                if (this.logger.isInfoEnabled()) {
                    this.logger.info("Overriding bean definition for bean '" + beanName +
                            "' with a different definition: replacing [" + oldBeanDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            else {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Overriding bean definition for bean '" + beanName +
                            "' with an equivalent definition: replacing [" + oldBeanDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }

            // 允许覆盖,直接覆盖原有的 BeanDefinition
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        /**
         * 不存在
         */
        else {

             // 检测创建 Bean 阶段是否已经开启,如果开启了则需要对 beanDefinitionMap 进行并发控制
            if (hasBeanCreationStarted()) {
                // beanDefinitionMap 为全局变量,避免并发情况
                synchronized (this.beanDefinitionMap) {
                    //
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    if (this.manualSingletonNames.contains(beanName)) {
                        Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                        updatedSingletons.remove(beanName);
                        this.manualSingletonNames = updatedSingletons;
                    }
                }
            }
            else {
                // 不会存在并发情况,直接设置
                this.beanDefinitionMap.put(beanName, beanDefinition);
                this.beanDefinitionNames.add(beanName);
                this.manualSingletonNames.remove(beanName);
            }
            this.frozenBeanDefinitionNames = null;
        }

        if (oldBeanDefinition != null || containsSingleton(beanName)) {
            // 重新设置 beanName 对应的缓存
            resetBeanDefinition(beanName);
        }
    }

处理过程如下:

  • 首先 BeanDefinition 进行校验,该校验也是注册过程中的最后一次校验了,主要是对 AbstractBeanDefinition 的 methodOverrides 属性进行校验
  • 根据 beanName 从缓存中获取 BeanDefinition,如果缓存中存在,则根据 allowBeanDefinitionOverriding 标志来判断是否允许覆盖,如果允许则直接覆盖,否则抛出 BeanDefinitionStoreException 异常
  • 若缓存中没有指定 beanName 的 BeanDefinition,则判断当前阶段是否已经开始了 Bean 的创建阶段(),如果是,则需要对 beanDefinitionMap 进行加锁控制并发问题,否则直接设置即可。对于 hasBeanCreationStarted() 方法后续做详细介绍,这里不过多阐述。
  • 若缓存中存在该 beanName 或者 单利 bean 集合中存在该 beanName,则调用 resetBeanDefinition() 重置 BeanDefinition 缓存。

其实整段代码的核心就在于 this.beanDefinitionMap.put(beanName, beanDefinition); 。BeanDefinition 的缓存也不是神奇的东西,就是定义 map ,key 为 beanName,value 为 BeanDefinition。

注册 alias

BeanDefinitionRegistry.registerAlias 完成 alias 的注册。

 public void registerAlias(String name, String alias) {
        // 校验 name 、 alias
        Assert.hasText(name, "'name' must not be empty");
        Assert.hasText(alias, "'alias' must not be empty");
        synchronized (this.aliasMap) {
            // name == alias 则去掉alias
            if (alias.equals(name)) {
                this.aliasMap.remove(alias);
            }
            else {
                // 缓存缓存记录
                String registeredName = this.aliasMap.get(alias);
                if (registeredName != null) {
                    // 缓存中的相等,则直接返回
                    if (registeredName.equals(name)) {
                        // An existing alias - no need to re-register
                        return;
                    }
                    // 不允许则抛出异常
                    if (!allowAliasOverriding()) {
                        throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" +
                                name + "': It is already registered for name '" + registeredName + "'.");
                    }
                }
                // 当 A --> B 存在时,如果再次出现 A --> B --> C 则抛出异常
                checkForAliasCircle(name, alias);
                // 注册 alias
                this.aliasMap.put(alias, name);
            }
        }
    }

注册 alias 和注册 BeanDefinition 的过程差不多。在最好调用了 checkForAliasCircle() 来对别名进行了检测。

  public boolean hasAlias(String name, String alias) {
        for (Map.Entry<String, String> entry : this.aliasMap.entrySet()) {
            String registeredName = entry.getValue();
            if (registeredName.equals(name)) {
                String registeredAlias = entry.getKey();
                return (registeredAlias.equals(alias) || hasAlias(registeredAlias, alias));
            }
        }
        return false;
    }

如果 name 、 alias 为 1 、3 ,则构成 (1,3),加入集合中存在(A,1)、(3,A)的情况则会出错。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值