zoukankan      html  css  js  c++  java
  • Spring5源码分析(012)——IoC篇之解析bean标签:解析 BeanDefinition

    注:《Spring5源码分析》汇总可参考:Spring5源码分析(002)——博客汇总


      bean 标签解析的第一步,就是委托 BeanDefinitionParserDelegate.parseBeanDefinitionElement(Element ele) 方法进行元素解析,返回 BeanDefinitionHolder 实例对象 bdHolder ,即 解析 BeanDefinition 。

    • 如果解析成功,则 bdHolder 实例已经包含配置文件的各种属性,例如 class 、 name 、 id 、 alias 之类的属性。
      • BeanDefinitionHolder 内部有 beanDefinition 、 beanName 、 aliases 3 个属性,而其中的 beanDefinition 则是 bean 的具体定义。
    • 如果解析失败,则 bdHolder 实例为 null 。

    接下来会对 parseBeanDefinitionElement(Element ele) 方法分析。本文目录结构如下:

    1、parseBeanDefinitionElement(Element ele)

      BeanDefinitionParserDelegate.parseBeanDefinitionElement(Element ele) 方法,对 <bean> 标签进行元素解析,也即是解析 BeanDefinition 的过程,代码如下:

    /**
     * Parses the supplied {@code <bean>} element. May return {@code null}
     * if there were errors during parse. Errors are reported to the
     * {@link org.springframework.beans.factory.parsing.ProblemReporter}.
     * <p>解析提供的 <bean> 标签元素。如果在解析过程中出现错误,可以返回 null 。
     * 错误将会报告给 {@link org.springframework.beans.factory.parsing.ProblemReporter} 进行处理。
     */
    @Nullable
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
        return parseBeanDefinitionElement(ele, null);
    }
    
    /**
     * Parses the supplied {@code <bean>} element. May return {@code null}
     * if there were errors during parse. Errors are reported to the
     * {@link org.springframework.beans.factory.parsing.ProblemReporter}.
     */
    @Nullable
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
        // 1.1、解析 id 、 name 属性
        String id = ele.getAttribute(ID_ATTRIBUTE);
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
    
        // 1.2、分割 name 属性,作为别名 alias 集合
        List<String> aliases = new ArrayList<>();
        if (StringUtils.hasLength(nameAttr)) {
            String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            aliases.addAll(Arrays.asList(nameArr));
        }
        // 1.3、beanName : 先默认使用 id
        String beanName = id;
        if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
            // 若 id 没有设置,则使用别名 alias 的第一个,同时移除这个别名
            beanName = aliases.remove(0);
            if (logger.isTraceEnabled()) {
                logger.trace("No XML 'id' specified - using '" + beanName +
                        "' as bean name and " + aliases + " as aliases");
            }
        }
        // 1.4、检查 beanName 和 alias 的唯一性
        if (containingBean == null) {
            checkNameUniqueness(beanName, aliases, ele);
        }
        // 2、解析属性,构造 AbstractBeanDefinition 对象
        AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
        if (beanDefinition != null) {
            // 再次检查 beanName
            if (!StringUtils.hasText(beanName)) {
                try {
                    if (containingBean != null) {
                        // 3.1、beanName 还是不存在,则根据 Spring 提供的命名规则生成唯一的 beanName ,
                        // 这个是内部 bean ,比较常见的格式就是: 类全限定名#十六进制字符串(对象的标识哈希码)
                        beanName = BeanDefinitionReaderUtils.generateBeanName(
                                beanDefinition, this.readerContext.getRegistry(), true);
                    }
                    else {
                        // 3.2、生成唯一的 beanName ,比较常见的格式就是: 类全限定名#counter(从0开始)
                        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.
                        // 增加别名 beanClassName ,兼容 1.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.isTraceEnabled()) {
                        logger.trace("Neither XML 'id' nor 'name' specified - " +
                                "using generated bean name [" + beanName + "]");
                    }
                }
                catch (Exception ex) {
                    error(ex.getMessage(), ele);
                    return null;
                }
            }
            // 4、创建 BeanDefinitionHolder 对象
            String[] aliasesArray = StringUtils.toStringArray(aliases);
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        }
    
        return null;
    }

      这个方法大致可以分为3个部分,实际的 bean 解析也只是进行了委托处理。这 3 个部分处理如下:

    • 1、beanName 和 aliad 的确定,即 beanNam 的命名规则,上面注释中的 1.1-1.4 和 3.1-3.2 便是这部分,命名规则如下
      • 1.1、如果 <bean /> 标签的属性 id 不为空,则 beanName = id
      • 1.2和1.3、如果 id 为空,但是 name 属性不为空,则会将其第一个配置设置为 beanName ,其他的依旧为 alias 。然后检查 id 和 alias 的唯一性。
      • 1.4、如果都没有,则在解析完 bean 之后使用默认规则来设置 beanName:
        • 如果是 innerBean ,则生成规则是:类全限定名#十六进制字符串(对象的标识哈希码)
        • 如不是,则为:类全限定名#counter(从0开始)。
        • 具体参考 : BeanDefinitionReaderUtils.generateBeanName(...)
      • 检查 id 和 alias 唯一性的代码如下:
    /**
     * Validate that the specified bean name and aliases have not been used already
     * within the current level of beans element nesting.
     */
    protected void checkNameUniqueness(String beanName, List<String> aliases, Element beanElement) {
        // 检查 beanName 和 alias 是否已经被使用
        String foundName = null;
    
        if (StringUtils.hasText(beanName) && this.usedNames.contains(beanName)) {
            foundName = beanName;
        }
        if (foundName == null) {
            foundName = CollectionUtils.findFirstMatch(this.usedNames, aliases);
        }
        // 如果 beanName 已存在,则使用 problemReporter 提示错误
        if (foundName != null) {
            error("Bean name '" + foundName + "' is already used in this <beans> element", beanElement);
        }
        // 将 beanName 和 alias 添加到 usedNames 集合中
        this.usedNames.add(beanName);
        this.usedNames.addAll(aliases);
    }
    • 2、解析属性,构造 AbstractBeanDefinition 对象:此处调用了 parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean) 方法,对 bean 标签进行解析并封装成 AbstractBeanDefinition 实例 beanDefinition ,实则是 GenericBeanDefinition 实例对象后面会及进行分析。
    • 3、根据 beanDefinition 、 beanName 、 aliases 构造 BeanDefinitionHolder 实例对象,然后返回。前面提到 BeanDefinitionHolder 有如下内部属性用于承载 bean:
    private final BeanDefinition beanDefinition;
    
    private final String beanName;
    
    @Nullable
    private final String[] aliases;

    2、parseBeanDefinitionElement( Element ele, String beanName, @Nullable BeanDefinition containingBean)

      这个方法就是 bean 解析的重头戏了,这里对 bean 标签的属性和子元素一一进行解析然后封装成 AbstractBeanDefinition 类型的 GenericBeanDefinition 实例对象,代码如下:

    /**
     * Parse the bean definition itself, without regard to name or aliases. May return
     * {@code null} if problems occurred during the parsing of the bean definition.
     */
    @Nullable
    public AbstractBeanDefinition parseBeanDefinitionElement(
            Element ele, String beanName, @Nullable BeanDefinition containingBean) {
    
        this.parseState.push(new BeanEntry(beanName));
    
        // 解析 class 属性
        String className = null;
        if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
            className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
        }
        // 解析 parent 属性
        String parent = null;
        if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
            parent = ele.getAttribute(PARENT_ATTRIBUTE);
        }
    
        try {
            // 创建用于承载属性的 AbstractBeanDefinition 类型的 GenericBeanDefinition 实例对象
            AbstractBeanDefinition bd = createBeanDefinition(className, parent);
            // 解析默认 bean 标签的各种属性
            parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
            // 提取 description
            bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
    
            /// 下面这些是解析 bean 标签的子元素
    
            // 解析元数据 <meta />
            parseMetaElements(ele, bd);
            // 解析 lookup-method 子元素 <lookup-method />
            parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
            // 解析 replaced-method 子元素 <replaced-method />
            parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
    
            // 解析构造函数参数 <constructor-arg />
            parseConstructorArgElements(ele, bd);
            // 解析 property 子元素 <property />
            parsePropertyElements(ele, bd);
            // 解析 qualifier 子元素 <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;
    }

      到这里已经看到了 XML 配置中, bean 标签的很多属性以及各种子元素了。这里解析出来的 BeanDefinition 可以算是基本成型了。

    2.1、createBeanDefinition

      解析之前,当然就是先创建一个能够承载各种属性的 BeanDefinition 了,这里使用的是 AbstractBeanDefinition 类型的 GenericBeanDefinition 实例对象:

    /**
     * Create a bean definition for the given class name and parent name.
     * @param className the name of the bean class
     * @param parentName the name of the bean's parent bean
     * @return the newly created bean definition
     * @throws ClassNotFoundException if bean class resolution was attempted but failed
     */
    protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
            throws ClassNotFoundException {
    
        return BeanDefinitionReaderUtils.createBeanDefinition(
                parentName, className, this.readerContext.getBeanClassLoader());
    }
    
    /// org.springframework.beans.factory.support.BeanDefinitionReaderUtils
    
    /**
     * Create a new GenericBeanDefinition for the given parent name and class name,
     * eagerly loading the bean class if a ClassLoader has been specified.
     * @param parentName the name of the parent bean, if any
     * @param className the name of the bean class, if any
     * @param classLoader the ClassLoader to use for loading bean classes
     * (can be {@code null} to just register bean classes by name)
     * @return the bean definition
     * @throws ClassNotFoundException if the bean class could not be loaded
     */
    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;
    }

    3、总结

      至此已初步看到了 bean 标签的各种属性和子元素的解析委托了,而 bean 标签的属性和子元素比较多,后续将继续进行注意的分析。

    4、参考

  • 相关阅读:
    【SAS NOTE】OUTPUT
    【SAS NOTES】_NULL_
    【SAS NOTE】sas 9.2 安装
    【SAS NOTE】FREQ
    纯数学教程 Page 203 例XLI (1)
    纯数学教程 Page 203 例XLI (3)
    纯数学教程 Page 203 例XLI (2)
    Prove Cauchy's inequality by induction
    纯数学教程 Page 325 例LXVIII (15) 调和级数发散
    纯数学教程 Page 325 例LXVIII (15) 调和级数发散
  • 原文地址:https://www.cnblogs.com/wpbxin/p/13534586.html
Copyright © 2011-2022 走看看