1
头图

opening

The previous article explained that the tags in Spring include custom tags and default tags. These two methods are quite different, so this article mainly explains the parsing process of default tags.

The parsing of the default tag is in the parseDefaultElement method.

ParseBeanDefinitions方法

This method treats different labels differently.

 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)) {
      doRegisterBeanDefinitions(ele);
   }
}

Bean tag parsing and registration

Of these four, we mainly focus on the parsing of bean tags . The parsing of bean tags is the most complex and important. We enter the processBeanDefinition method.

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

Let's look at the sequence diagram first for this code.

Bean标签的解析及注册时序图

The general logic of the method processBeanDefinition is as follows:

  1. First, the delegate.parseBeanDefinitionElement(ele) method is called for element analysis. And return the bdHolder of the BeanDefinitionHolder type. After this method, the bdHolder instance already contains various attributes in the configuration file, such as class, name, id, alias, etc.
  2. When the returned bdHolder is not empty, if there is a custom attribute under the child node of the default label, the custom label needs to be parsed.
  3. After the parsing is completed, the parsed bdHolder needs to be registered, and the registration operation is delegated to the BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); method
  4. Finally, a response event is issued to notify the relevant listeners that the bean has been loaded

Parse BeanDefinition

Next, we analyze a little bit, first we analyze the method delegate.parseBeanDefinitionElement(ele) .

This method is in the BeanDefinitionParserDelegate class.

 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
   return parseBeanDefinitionElement(ele, null);
}
 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.isTraceEnabled()) {
         logger.trace("No XML 'id' specified - using '" + beanName +
               "' as bean name and " + aliases + " as aliases");
      }
   }

   if (containingBean == null) {
      checkNameUniqueness(beanName, aliases, ele);
   }
   // 代码(1)
   AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
   if (beanDefinition != null) {
      if (!StringUtils.hasText(beanName)) {
         try {
            //如果不存在beanName那么根据Spring中提供的命名规则为当前bean生成对应的beanName
            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.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;
}

This method is the whole process of parsing default tags, we can now see the parsing of attributes id and name.

The main content completed in the current method is as follows:

  1. Extract element id, name attributes
  2. Parse other properties and encapsulate them into GenericBeanDefinition type instances
  3. If a bean is detected without specifying a beanName, a beanName is generated using the default rules
  4. Encapsulate the obtained information into a BeanDefinitionHolder instance

Let's take a look at how the parseBeanDefinitionElement method called by the code (1) marked in the code parses other tags.

 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 {
      //代码(1)创建用于承载属性的AbstractBeanDefinition类型的GenericBeanDefinition
      AbstractBeanDefinition bd = createBeanDefinition(className, parent);
            //代码(2)解析默认bean的各种属性
      parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
      //提取 description
      bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
      //代码(3)解析元数据
      parseMetaElements(ele, bd);
      //解析lookup-medthod属性 (用的很少,这里就不深入介绍)
      parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
      //解析replace-medthod属性(用的很少,这里就不深入介绍)
      parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
      //代码(4)解析构造函数参数
      parseConstructorArgElements(ele, bd);
      //代码(5)解析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;
}

Create a BeanDefinition for property hosting

Before we look at the method called by code (1), let's take a look at the BeanDefinition.

BeanDefinition is an interface, there are three implementations in Spring: RootBeanDefinition, ChildBeanDefinition, GenericBeanDefinition. They all inherit from AbstractBeanDefinition , where BeanDefinition is the representation of the configuration file <bean> element inside the container. This tag has configuration attributes such as class, scope, lazy-init, etc. BeanDefinition also provides corresponding attributes: beanClass, scope, lazyInit.

Among them, RootBeanDefinition is the most commonly used implementation class, which generally corresponds to the <bean> element tag, while GenericBeanDefinition is a bean file configuration attribute definition class added after version 2.5, providing a one-stop service class.

In the configuration file, we can parent <bean> and child <bean> , the parent is represented by RootBeanDefinition, and the child is represented by ChildBeanDefinition. Ordinary <bean> is represented by RootBeanDefinition, while AbstractBeanDefinition abstracts the information of the two common classes.

Spring converts the configuration file <bean> into the container's internal representation through BeanDefinition, and registers these BeanDefinitions in the BeanDefinitionRegistry.

The BeanDefinitionRegistry of the Spring container is mainly stored in the form of map, and subsequent operations can directly obtain configuration information from this class.

But first, before we parse the property, we need to create an instance for carrying the property, which is to create an instance of the GenericBeanDefinition type we mentioned earlier. That is, the createBeanDefinition(className, parent) method called by code (1).

 protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
      throws ClassNotFoundException {

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

   GenericBeanDefinition bd = new GenericBeanDefinition();
   //如果没有父类,parentName则为空
   bd.setParentName(parentName);
   if (className != null) {
      //如果classLoader不为空则使用传入的classLoader进行加载类对象,否则只是记录className
      if (classLoader != null) {
         bd.setBeanClass(ClassUtils.forName(className, classLoader));
      }else {
         bd.setBeanClassName(className);
      }
   }
   return bd;
}

So far, we have created a GenericBeanDefinition instance.

Parse various properties

After the GenericBeanDefinition instance used to carry the bean information is created, various properties of the bean information can be parsed.

First we enter the code (2) parseBeanDefinitionAttributes method, which parses all element attributes of element.

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

    // 解析singleton属性
   if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
      // singleton属性已经不被支持,使用scope代替
      error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
   }
   //解析scope属性
   else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
      bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
   }
   else if (containingBean != null) {
      //在嵌入BeanDefinition情况下,并且没有单独指定scope属性,则使用父类默认的属性
      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 (isDefaultValue(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 (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));
   }

    // 解析primary属性
   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-method属性
   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));
   }
   // 解析factory-bean属性
   if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
      bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
   }

   return bd;
}

The main purpose of this method is to get the attribute values corresponding to various attributes and put them into the corresponding attributes of AbstractBeanDefinition.

Parse child element meta

First let's review how to use the meta attribute.

 <bean id="myTestBean" class="cn.jack.MyTestBean">
        <meta key="jack" value="HelloWorld"/>
</bean>
 public class MyTestBean {

    private String testStr = "testStr";

    public String getTestStr() {
        return testStr;
    }

    public void setTestStr(String testStr) {
        this.testStr = testStr;
    }
}

This code is not reflected in MyTestBean, it is just a statement, which can be obtained by the 使用BeanDefinition类的getAttribute(key) method when it is used.

Next, let's take a look at how it is parsed and enter code (3).

 public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
   //获取当前节点所有元素
   NodeList nl = ele.getChildNodes();
   for (int i = 0; i < nl.getLength(); i++) {
      Node node = nl.item(i);
      //判断节点是否为meta
      if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
         Element metaElement = (Element) node;
         String key = metaElement.getAttribute(KEY_ATTRIBUTE);
         String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
         //构造BeanMetadataAttribute实例
         BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
         attribute.setSource(extractSource(metaElement));
         //记录信息
         attributeAccessor.addMetadataAttribute(attribute);
      }
   }
}

Parse child element constructor-arg

The parsing of constructors is still very common, but also very complex, for example:

 <bean id="myTestBean" class="cn.jack.MyTestBean">
        <constructor-arg index="0">
            <value>Jack</value>
        </constructor-arg>
        <constructor-arg index="1">
            <value>hello</value>
        </constructor-arg>
</bean>

This code is the most basic configuration in Spring. It automatically finds the corresponding constructor and passes in the set parameters during initialization. Next, let's see how to parse it.

 public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
   //拿到bean所有子节点
   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)) {
         //解析constructor-arg
         parseConstructorArgElement((Element) node, bd);
      }
   }
}

Enter the parseConstructorArgElement method.

 public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
        //提取index属性
        String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
        //提取type属性
        String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
        //提取name属性
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
        if (StringUtils.hasLength(indexAttr)) {
            try {
                int index = Integer.parseInt(indexAttr);
                if (index < 0) {
                    error("'index' cannot be lower than 0", ele);
                }
                else {
                    try {
                        this.parseState.push(new ConstructorArgumentEntry(index));
                        //代码(1)解析ele对应的属性元素
                        Object value = parsePropertyValue(ele, bd, null);
                        //使用ConstructorArgumentValues.ValueHolder类型封装解析出来的元素
                        ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
                        //将name属性和type都封装到valueHolder中
                        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 {
                            //添加到BeanDefinition的ConstructorArgumentValues中,存入结构为Map
                            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 {
            //index为空的处理
            try {
                this.parseState.push(new ConstructorArgumentEntry());
                //解析ele节点对应的属性值
                Object value = parsePropertyValue(ele, bd, null);
                //使用ConstructorArgumentValues.ValueHolder类型封装解析出来的元素
                ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
                //将name属性和type都封装到valueHolder中
                if (StringUtils.hasLength(typeAttr)) {
                    valueHolder.setType(typeAttr);
                }
                if (StringUtils.hasLength(nameAttr)) {
                    valueHolder.setName(nameAttr);
                }
                valueHolder.setSource(extractSource(ele));
                //添加到BeanDefinition的ConstructorArgumentValues中,因为没有index则存入结构为List
                bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
            }
            finally {
                this.parseState.pop();
            }
        }
}

This method is not particularly complicated, it first extracts the necessary attributes (index, type, name) on the constructor-arg.

If index is specified in the configuration, the operation steps are as follows:

  1. Parse the child elements of constructor-arg
  2. Use the ConstructorArgumentValues.ValueHolder type to encapsulate the parsed element
  3. Finally, encapsulate the index, name, and type into the ValueHolder type, and add them to the indexedArgumentValues property of the constructorArgumentValues of the BeanDefinition.

If the index is not specified in the configuration, the operation steps are as follows:

  1. Parse the child elements of constructor-arg
  2. Use the ConstructorArgumentValues.ValueHolder type to encapsulate the parsed element
  3. Finally, encapsulate the index, name, and type into the ValueHolder type, and add them to the genericArgumentValues property of the constructorArgumentValues of the BeanDefinition.

After understanding the process, let's take a look at how to parse it, and enter the method of code (1) parsePropertyValue .

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

   // 获取ele节点的子节点,一个属性只能对应一种类型:ref/value/list等
   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)) {
         //只能有一个子节点,否则异常
         if (subElement != null) {
            error(elementName + " must not contain more than one sub-element", ele);
         }
         else {
            //把子节点赋值给subElement
            subElement = (Element) node;
         }
      }
   }
       //解析constructor-arg的ref属性
   boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
   //解析constructor-arg的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属性处理,使用RuntimeBeanReference封装对应的ref名称
      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) {
      //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 {
      //如果没有ref和value,也没有子元素则抛出异常
      // Neither child element nor "ref" or "value" attribute found.
      error(elementName + " must specify a ref or value", ele);
      return null;
   }
}

This method parses the element of the attribute in the constructor, and goes through the following process:

  1. skip description or meta
  2. Extract the ref and value attributes on constructor-arg, and then verify
  3. For ref attribute processing, use RuntimeBeanReference to encapsulate the corresponding ref name, for example:

     <constructor-arg ref="a"></constructor-arg>
  4. The processing of the value attribute is encapsulated by TypedStringValue, for example:

     <constructor-arg value="a"></constructor-arg>
  5. Child element processing, such as:

     <bean id="myTestBean" class="cn.jack.MyTestBean">
            <constructor-arg>
                <map>
                    <entry key="jack" value="nihao"></entry>
                </map>
            </constructor-arg>
    </bean>

For the processing of child elements, such as the addition of the map element mentioned here, how is it handled? Specifically, the processing of various sub-elements is implemented in 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);
        }
        //解析是否为bean节点
        else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
            BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
            if (nestedBd != null) {
                nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
            }
            return nestedBd;
        }
        //解析ref标签
        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.
                //解析parent
                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;
        }
        //解析idref元素
        else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
            return parseIdRefElement(ele);
        }
        //解析value元素
        else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
            return parseValueElement(ele, defaultValueType);
        }
        //解析null元素
        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;
        }
        //解析array元素
        else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
            return parseArrayElement(ele, bd);
        }
        //解析list元素
        else if (nodeNameEquals(ele, LIST_ELEMENT)) {
            return parseListElement(ele, bd);
        }
        //解析set元素
        else if (nodeNameEquals(ele, SET_ELEMENT)) {
            return parseSetElement(ele, bd);
        }
        //解析map元素
        else if (nodeNameEquals(ele, MAP_ELEMENT)) {
            return parseMapElement(ele, bd);
        }
        //解析props元素
        else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
            return parsePropsElement(ele);
        }
        else {
            error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
            return null;
        }
}

In this method, the classification processing of all supported types is implemented. At this point, it has been clear how the constructor is parsed. Here, we will not delve into how to parse elements such as list and map.

Parse child element property

After analyzing the constructor, we can continue to look down, here to avoid forgetting where we are now.

parseBeanDefinitionElement

Here we first review how to use the property attribute. Of course, elements such as list can also be used in the property attribute.

 <bean id="myTestBean" class="cn.jack.MyTestBean">
   <property name="testStr" value="jack"/>
</bean>
 public class MyTestBean {

   private String testStr = "testStr";

   public String getTestStr() {
      return testStr;
   }

   public void setTestStr(String testStr) {
      this.testStr = testStr;
   }
}

Next, let's see how it is parsed.

 public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
   //获取到beanElement的所有子节点
   NodeList nl = beanEle.getChildNodes();
   for (int i = 0; i < nl.getLength(); i++) {
      Node node = nl.item(i);
      if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
         //解析property节点
         parsePropertyElement((Element) node, bd);
      }
   }
}
 public void parsePropertyElement(Element ele, BeanDefinition bd) {
   //获取配置元素的name值
   String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
   if (!StringUtils.hasLength(propertyName)) {
      //name为空则抛出异常
      error("Tag 'property' must have a 'name' attribute", ele);
      return;
   }
   this.parseState.push(new PropertyEntry(propertyName));
   try {
      //校验在相同bean节点下,是否存在同样的name属性,如果存在则抛出异常
      if (bd.getPropertyValues().contains(propertyName)) {
         error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
         return;
      }
      //解析属性值
      Object val = parsePropertyValue(ele, bd, propertyName);
      //解析后的值和name属性封装为PropertyValue
      PropertyValue pv = new PropertyValue(propertyName, val);
      //解析meta节点
      parseMetaElements(ele, pv);
      pv.setSource(extractSource(ele));
      //解析完成后添加到BeanDefinition的propertyValues属性中
      bd.getPropertyValues().addPropertyValue(pv);
   }
   finally {
      this.parseState.pop();
   }
}

The process is similar to the previous explanation. All sub-tags are obtained first, then traversed for analysis, and the corresponding name and value values are obtained for packaging.

Parse sub-element qualifier

We generally use more annotations for this element, mainly when there are multiple implementation classes in the interface, we specify an implementation class when we inject, so that the Spring container can find the corresponding bean. Because the number of Bean candidates in Spring must be one and only one. The parsing process is similar to the previous one and will not be repeated here.

 <bean id="myTestBean" class="cn.jack.MyTestBean">
   <qualifier type="org.springframework.beans.factory.annotation.Qualifier" value="Bean的名称"/>
</bean>

AbstractBeanDefinition property

 AbstractBeanDefinition bd = createBeanDefinition(className, parent);

So far, we have completed the conversion of XML document to GenericBeanDefinition, the configuration in XML can be seen in GenericBeanDefinition, but GenericBeanDefinition is just a subclass, most of the properties are in AbstractBeanDefinition. Let's review what configurations are available.

 public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
        implements BeanDefinition, Cloneable {

    // 此处省略静态变量以及final变量

    @Nullable
    private volatile Object beanClass;
    /**
     * bean的作用范围,对应bean属性scope
     */
    @Nullable
    private String scope = SCOPE_DEFAULT;
    /**
     * 是否是抽象,对应bean属性abstract
     */
    private boolean abstractFlag = false;
    /**
     * 是否延迟加载,对应bean属性lazy-init
     */
    private boolean lazyInit = false;
    /**
     * 自动注入模式,对应bean属性autowire
     */
    private int autowireMode = AUTOWIRE_NO;
    /**
     * 依赖检查,Spring 3.0后弃用这个属性
     */
    private int dependencyCheck = DEPENDENCY_CHECK_NONE;
    /**
     * 用来表示一个bean的实例化依靠另一个bean先实例化,对应bean属性depend-on
     */
    @Nullable
    private String[] dependsOn;
    /**
     * autowire-candidate属性设置为false,这样容器在查找自动装配对象时,
     * 将不考虑该bean,即它不会被考虑作为其他bean自动装配的候选者,
     * 但是该bean本身还是可以使用自动装配来注入其他bean的
     */
    private boolean autowireCandidate = true;
    /**
     * 自动装配时出现多个bean候选者时,将作为首选者,对应bean属性primary
     */
    private boolean primary = false;
    /**
     * 用于记录Qualifier,对应子元素qualifier
     */
    private final Map<String, AutowireCandidateQualifier> qualifiers = new LinkedHashMap<>(0);

    @Nullable
    private Supplier<?> instanceSupplier;
    /**
     * 允许访问非公开的构造器和方法,程序设置
     */
    private boolean nonPublicAccessAllowed = true;
    /**
     * 是否以一种宽松的模式解析构造函数,默认为true,
     * 如果为false,则在以下情况
     * interface ITest{}
     * class ITestImpl implements ITest{};
     * class Main {
     *     Main(ITest i) {}
     *     Main(ITestImpl i) {}
     * }
     * 抛出异常,因为Spring无法准确定位哪个构造函数程序设置
     */
    private boolean lenientConstructorResolution = true;
    /**
     * 对应bean属性factory-bean,用法:
     * <bean id = "instanceFactoryBean" class = "example.chapter3.InstanceFactoryBean" />
     * <bean id = "currentTime" factory-bean = "instanceFactoryBean" factory-method = "createTime" />
     */
    @Nullable
    private String factoryBeanName;
    /**
     * 对应bean属性factory-method
     */
    @Nullable
    private String factoryMethodName;
    /**
     * 记录构造函数注入属性,对应bean属性constructor-arg
     */
    @Nullable
    private ConstructorArgumentValues constructorArgumentValues;
    /**
     * 普通属性集合
     */
    @Nullable
    private MutablePropertyValues propertyValues;
    /**
     * 方法重写的持有者,记录lookup-method、replaced-method元素
     */
    @Nullable
    private MethodOverrides methodOverrides;
    /**
     * 初始化方法,对应bean属性init-method
     */
    @Nullable
    private String initMethodName;
    /**
     * 销毁方法,对应bean属性destroy-method
     */
    @Nullable
    private String destroyMethodName;
    /**
     * 是否执行init-method,程序设置
     */
    private boolean enforceInitMethod = true;
    /**
     * 是否执行destroy-method,程序设置
     */
    private boolean enforceDestroyMethod = true;
    /**
     * 是否是用户定义的而不是应用程序本身定义的,创建AOP时候为true,程序设置
     */
    private boolean synthetic = false;
    /**
     * 定义这个bean的应用,APPLICATION:用户,INFRASTRUCTURE:完全内部使用,与用户无关,
     * SUPPORT:某些复杂配置的一部分
     * 程序设置
     */
    private int role = BeanDefinition.ROLE_APPLICATION;
    /**
     * bean的描述信息
     */
    @Nullable
    private String description;
    /**
     * 这个bean定义的资源
     */
    @Nullable
    private Resource resource;
}

Parse custom label element in default label

processBeanDefinition

So far, we have analyzed BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); this line of code, and then we continue to analyze bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); this line of code.

Let's first understand the general function of this line of code and analyze it semantically: if necessary, decorate the BeanDefinition, similar to the following scenario:

 <bean id="myTestBean" class="cn.jack.MyTestBean">
        <mybean:user username="jack"/>
</bean>

This line of code executes when the bean in Spring uses the default tag configuration, but the child elements use custom configuration.

But why add a separate custom type to the default type resolution? First of all, this custom type does not appear in the form of a bean. The custom type here is actually equivalent to an attribute.

We continue to analyze the method code.

 public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef) {
   return decorateBeanDefinitionIfRequired(ele, originalDef, null);
}

When calling the decorateBeanDefinitionIfRequired method, the third parameter is passed in as null, which is the parent class bean. When analyzing a nested configuration, you need to pass the parent class BeanDefinition, in fact, to use the parent class The scope property of the parent class is used if the subclass does not set the scope property. Here is the top-level configuration, so pass null.

 public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
            Element ele, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {

        BeanDefinitionHolder finalDefinition = originalDef;

        // 遍历节点,查看是否存在适用于装饰的属性
        // Decorate based on custom attributes first.
        NamedNodeMap attributes = ele.getAttributes();
        for (int i = 0; i < attributes.getLength(); i++) {
            Node node = attributes.item(i);
            finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
        }
        //遍历子节点,查看是否存在适用于装饰的属性
        // Decorate based on custom nested elements.
        NodeList children = ele.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            Node node = children.item(i);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
            }
        }
        return finalDefinition;
}

Finally, the decorateIfRequired method is called, and we enter this method to check.

 public BeanDefinitionHolder decorateIfRequired(
      Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
       //获取自定义命名空间
   String namespaceUri = getNamespaceURI(node);
   // 过滤默认命名空间
   if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) {
      //根据命名空间找到相应的处理器
      NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
      if (handler != null) {
         //进行装饰处理
         BeanDefinitionHolder decorated =
               handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
         if (decorated != null) {
            return decorated;
         }
      }
      else if (namespaceUri.startsWith("http://www.springframework.org/schema/")) {
         error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);
      }
      else {
         // A custom namespace, not to be handled by Spring - maybe "xml:...".
         if (logger.isDebugEnabled()) {
            logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");
         }
      }
   }
   return originalDef;
}

At this point, it is very clear. First, get the namespace of the element or attribute, and then judge whether it is suitable for the parsing conditions of the custom tag, and then find the corresponding NamespaceHandler for the next step of parsing, which will be explained in the custom tag parsing.

Summary: The function of this method is to process custom tags or custom attributes, and then find the corresponding namespace processor for further analysis.

Register the resolved BeanDefinition

At this point, we have completed the analysis and decoration of the configuration file. Now the BeanDefinition has met the usage requirements, and the registration work is left in the future.

That is, the line of code in the processBeanDefinition method BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); .

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

   //获取beanName做唯一标识注册
   String beanName = definitionHolder.getBeanName();
   registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

   //如果有别名的话,注册所有别名
   String[] aliases = definitionHolder.getAliases();
   if (aliases != null) {
      for (String alias : aliases) {
         registry.registerAlias(beanName, alias);
      }
   }
}

After the method obtains the beanName, the final BeanDefinition will be registered in the BeanDefinitionRegistry . The method is divided into two parts, one is the beanName registration method and the alias registration method.

For the registration of BeanDefinition, not just put the BeanDefinition into the map, and then use the beanName as the key. In addition to doing other things.

Enter the DefaultListableBeanFactory class implementation.

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

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

        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                //注册前最后一次校验,针对AbstractBeanDefinition中的methodOverrides校验
                //校验methodOverrides是否与工厂方法并存,或者methodOverrides对应的方法压根不存在
                ((AbstractBeanDefinition) beanDefinition).validate();
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Validation of bean definition failed", ex);
            }
        }
        //判断是否已经存在bean
        BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
        if (existingDefinition != null) {
            //如果对应的beanName已经注册过并且不允许覆盖,则抛出异常
            if (!isAllowBeanDefinitionOverriding()) {
                throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
            }
            else if (existingDefinition.getRole() < beanDefinition.getRole()) {
                // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
                if (logger.isInfoEnabled()) {
                    logger.info("Overriding user-defined bean definition for bean '" + beanName +
                            "' with a framework-generated bean definition: replacing [" +
                            existingDefinition + "] with [" + beanDefinition + "]");
                }
            }
            else if (!beanDefinition.equals(existingDefinition)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Overriding bean definition for bean '" + beanName +
                            "' with a different definition: replacing [" + existingDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            else {
                if (logger.isTraceEnabled()) {
                    logger.trace("Overriding bean definition for bean '" + beanName +
                            "' with an equivalent definition: replacing [" + existingDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            //存入BeanDefinition
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        else {
            //是否已经开始创建bean
            if (hasBeanCreationStarted()) {
                // Cannot modify startup-time collection elements anymore (for stable iteration)
                // 因为beanDefinitionMap是全局变量,这里会存在并发访问的情况
                synchronized (this.beanDefinitionMap) {
                    //存入BeanDefinition
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    removeManualSingletonName(beanName);
                }
            }
            else {
                // Still in startup registration phase
                //存入BeanDefinition
                this.beanDefinitionMap.put(beanName, beanDefinition);
                //记录beanName
                this.beanDefinitionNames.add(beanName);
                //从factoryBeanCreatedCache中移除掉这个beanName
                removeManualSingletonName(beanName);
            }
            this.frozenBeanDefinitionNames = null;
        }

        if (existingDefinition != null || containsSingleton(beanName)) {
            // 重置所有beanName对应的缓存
            resetBeanDefinition(beanName);
        }
}

Registering a bean is divided into the following four steps:

  1. Check the methodOverrides property of AbstractBeanDefinition
  2. Throws an exception if it has been registered and overwriting is not allowed, otherwise overwrites directly
  3. Add map cache
  4. Clear the beanName cache before parsing

After that, we will see that registering through aliases is much simpler.

 public void registerAlias(String name, String alias) {
   Assert.hasText(name, "'name' must not be empty");
   Assert.hasText(alias, "'alias' must not be empty");
   synchronized (this.aliasMap) {
      //如果beanName与alias相同则不记录alias,并删除对应的alias
      if (alias.equals(name)) {
         this.aliasMap.remove(alias);
         if (logger.isDebugEnabled()) {
            logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
         }
      }
      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 define alias '" + alias + "' for name '" +
                     name + "': It is already registered for name '" + registeredName + "'.");
            }
            if (logger.isDebugEnabled()) {
               logger.debug("Overriding alias '" + alias + "' definition for registered name '" +
                     registeredName + "' with new target name '" + name + "'");
            }
         }
         //确保添加的没有name和alias值相反的数据且alias和name不相等
         checkForAliasCircle(name, alias);
         //存入map中
         this.aliasMap.put(alias, name);
         if (logger.isTraceEnabled()) {
            logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
         }
      }
   }
}

From this method, the steps to register alias are as follows:

  1. Alias and beanName are processed in the same situation, if they are the same, no need to process and delete the original alias
  2. Override check processing
  3. alias loop check
  4. register alias

Notify listener that parsing and registration are complete

通知监听器解析及注册完成

After the registration is completed, when the developer needs to monitor the registered BeanDefinition event, the processing logic can be written into the listener by registering the listener. Spring does not do any logic processing for this event.

Summarize

At this point, the Bean parsing and registration process is all OK.

To recap, the entry for parsing the BeanDefinition is at DefaultBeanDefinitionDocumentReader.parseBeanDefinitions() . This method will judge whether the label is a default label or a custom label according to the command space. The default label is implemented by parseDefaultElement() , and the custom label is implemented by parseCustomElement() . In the default tag parsing, the four major tags of import, alias, bean and beans will be processed according to the different tag names. The parsing of the bean tag is the core, which is implemented by the processBeanDefinition() method. processBeanDefinition() Start to enter the core work of parsing, which is divided into three steps:

  1. Parse default label: BeanDefinitionParserDelegate.parseBeanDefinitionElement()
  2. Parse custom tags under default tags: BeanDefinitionParserDelegate.decorateBeanDefinitionIfRequired()
  3. Register the resolved BeanDefinition: BeanDefinitionReaderUtils.registerBeanDefinition()

In the default tag parsing process, the core work is implemented by the parseBeanDefinitionElement() method, which parses the attributes and sub-elements of the bean tag in turn, and returns a GenericBeanDefinition instance object after the parsing is completed.

Finally, after registering the BeanDefinition through the registerBeanDefinition method, you are done.


神秘杰克
765 声望382 粉丝

Be a good developer.