preface

We should have a preliminary understanding of the Spring IOC container, next we study the Spring tag parsing, Spring tag is composed of default tags and custom tags, the two parsing is completely different, This article mainly explains the process of default label parsing

First, we need to know the parsing process is of default label DefaultBeanDefinitionDocumentReader parseDefaultElement method, this method of 4 kinds of labels (import, alisa describes, beans, and beans) did different analytical processing

  • See the source code (DefaultBeanDefinitionDocumentReader.java)
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
        importBeanDefinitionResource(ele);
    } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
        processAliasRegistration(ele);
    } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
        // Bean tag parsing and registration
        processBeanDefinition(ele, delegate);
    } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
        // recursedoRegisterBeanDefinitions(ele); }}Copy the code

Bean label parsing and registration

The above parseDefaultElement() method parses the four tags, of which the bean tag is the most complex and relatively important. So let’s start with the tag.

  • See the source code (DefaultBeanDefinitionDocumentReader.java)
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(newBeanComponentDefinition(bdHolder)); }}Copy the code

ProcessBeanDefinition: processBeanDefinition:

  1. Parsing BeanDefinition

    First entrust BeanDefinitionParserDelegate parseBeanDefinitionElement method for element analysis, return BeanDefinitionHolder instance: bdHolder; After this step, the bdHolder instance already contains various properties in our 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 resolved again. bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);

  3. When parsing is complete, the need to parse the bdHolder after register, this step registered entrusted to the BeanDefinitionReaderUtils registerBeanDefinition method; BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());

  4. Finally, a response event is issued to inform the relevant listener that the Bean has been loaded.

Let’s take a look at each step in detail.

1. The analytic BeanDefinition

We then a concrete analysis about the first step, element analysis and information extraction is BeanDefinitionHolder bdHolder = delegate. ParseBeanDefinitionElement (ele); To enter the function:

  • See the source code (BeanDefinitionParserDelegate.java)
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
    // Parse the ID attribute
    String id = ele.getAttribute(ID_ATTRIBUTE);
    // Parse the name attribute
    String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
    // Split the name attribute
    List<String> aliases = new ArrayList<>();
    if (StringUtils.hasLength(nameAttr)) {
        String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
        aliases.addAll(Arrays.asList(nameArr));
    }
    String beanName = id;
    if(! StringUtils.hasText(beanName) && ! aliases.isEmpty()) { beanName = aliases.remove(0);
        if (logger.isTraceEnabled()) {
            logger.trace("No XML 'id' specified - using '" + beanName +
                                    "' as bean name and " + aliases + " as aliases"); }}// check the uniqueness of name
    if (containingBean == null) {
        checkNameUniqueness(beanName, aliases, ele);
    }
    AbstractBeanDefinition is AbstractBeanDefinition
    AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
    if(beanDefinition ! =null) {
        // If beanName does not exist, construct a beanName based on the condition
        if(! StringUtils.hasText(beanName)) {try {
                if(containingBean ! =null) {
                    beanName = BeanDefinitionReaderUtils.generateBeanName(
                                                    beanDefinition, this.readerContext.getRegistry(), true);
                } else {
                    beanName = this.readerContext.generateBeanName(beanDefinition);
                    // Register an alias for the plain bean class name, if still possible,
                    // if the generator returned the class name plus a suffix.
                    // This is expected for Spring 1.2/2.0 backwards.
                    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);
        / / encapsulation BeanDefinitionHolder
        return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
    }
    return null;
}
Copy the code
  • Source code analysis

The above method is the element parsing and information extraction of the default tag, a simple summary of the above method:

  • Extract the ID and name attributes from the parsed element
  • The other attributes are further parsed and uniformly encapsulated in instances of GenericBeanDefinition(an AbstractBeanDefinition implementation class) types.
  • If it detects that the bean does not specify a beanName, use the default rules to generate a beanName for the bean,
  • Encapsulate the obtained information into an instance of BeanDefinitionHolder

The second step is the code above: AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement (ele, beanName containingBean); Parsing other attributes in the tag. Let’s see:

  • See the source code (BeanDefinitionParserDelegate.java)
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
            Element ele, String beanName, @Nullable BeanDefinition containingBean) {
    this.parseState.push(new BeanEntry(beanName));
    String className = null;
    // Parse the class attribute
    if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
        className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
    }
    String parent = null;
    // Parse the parent attribute
    if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
        parent = ele.getAttribute(PARENT_ATTRIBUTE);
    }
    try {
        // Create a GenericBeanDefinition for the AbstractBeanDefinition type of the property
        AbstractBeanDefinition bd = createBeanDefinition(className, parent);
        // Hardcode the various properties of the parse Bean
        parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
        // Set the description property
        bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
        // Parse meta elements
        parseMetaElements(ele, bd);
        // resolve the lookup-method property
        parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
        // Resolve the appet-method attribute
        parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
        // Parse the constructor arguments
        parseConstructorArgElements(ele, bd);
        // Parse the properties child element
        parsePropertyElements(ele, bd);
        Parse the qualifier child element
        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;
}
Copy the code

Let’s follow up on the steps in the first resolution of the BeanDefinition in detail.


Create a BeanDefinition to host the property

BeanDefinition is an interface, and there are three main implementations of this interface in Spring:

  1. RootBeanDefinition
  2. ChildBeanDefinition
  3. GenericBeanDefinition

All three of the above implementation classes inherit AbstractBeanDefinition, where BeanDefiniton is the internal representation of the configuration file element tag. Element tags have class, scope, and lazy-init attributes. BeanDefiniton provides the corresponding beanClass, Scope, and lazyInit attributes. BeanDefinition corresponds to the attributes in the bean tag. RootBeanDefinition is the most commonly used implementation class, which corresponds to the general element tag, and GenricBeanDefinition is the bean file configuration attribute definition class newly added after version 2.5, which is one-stop service.

In the configuration file, you can define parent and child. The parent is RootBeanDefinition, and the parent is ChildBeanDefinition, and the non-parent is RootBeanDefiniton. AbstractBeanDefinition abstracts the common class information of both.

Spring converts configuration information in a configuration file to an internal representation of a container through BeanDefiniton and registers these BeanDefinitionRegistry. ** The BeanDefinitionRegistry of the Spring container is like an in-memory database for Spring configuration information. ** It is mainly stored as a map, and subsequent operations read configuration information directly from BeanDefinitionRegistry. The relationship between them is shown below:

So, to parse a property, you first create an instance to host the property, that is, an instance of the GenricBeanDefinition type, AbstractBeanDefinition bd = createBeanDefinition(className, parent); This line of code does just that. Let’s look at the method body in detail:

  • See the source code (BeanDefinitionParserDelegate.java)
protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
            throws ClassNotFoundException {
    / / created here is realized in BeanDefinitionReaderUtils GenericBeanDefinition instance
    return BeanDefinitionReaderUtils.createBeanDefinition(
                    parentName, className, this.readerContext.getBeanClassLoader());
}
Copy the code
  • See the source code (BeanDefinitionReaderUtils.java)
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;
}
Copy the code

Once we’ve created the instance that holds the Bean information, it’s time to parse the various properties. We continue to go back to BeanDefinitionParserDelegate parseBeanDefinitionElement in class next to the various properties of analytical method.

Parsing of various attributes
  • See the source code (BeanDefinitionParserDelegate.java)
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
            @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
    // Parse the singleton attribute
    if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
        error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
    }
    Elseif (el.hasattribute (SCOPE_ATTRIBUTE)) {elseIf (el.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());
    }
    // Parse the abstract attribute
    if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
        bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
    }
    // Parse the lazyInit property
    String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
    if (isDefaultValue(lazyInit)) {
        lazyInit = this.defaults.getLazyInit();
    }
    bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
    // Parse the autowire attribute
    String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
    bd.setAutowireMode(getAutowireMode(autowire));
    // Resolve the dependsOn attribute
    if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
        String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
        bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
    }
    // Parse the autowireCandidate attribute
    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));
    }
    // Parse the primary attribute
    if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
        bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
    }
    // Parse the init_method property
    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);
    }
    // Resolve the destroy_method property
    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);
    }
    // Parse the factory_method attribute
    if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
        bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
    }
    // Parse the factory_bean properties
    if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
        bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
    }
    return bd;
}
Copy the code

After parsing the Bean’s various properties, we set description, which we won’t expand here, and then parse the meta element. Let’s see

Parsing meta Elements
  • See the source code (BeanDefinitionParserDelegate.java)
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 = newBeanMetadataAttribute(key, value); attribute.setSource(extractSource(metaElement)); attributeAccessor.addMetadataAttribute(attribute); }}}Copy the code

Here’s a quick look at using meta elements:

<bean id="demo" class="com.vipbbo.hellospring.demo">
      <property name="beanName" value="bean demo1"/>
      <meta key="demo" value="demo"/>
</bean>
Copy the code

This code is not present in the demo property, but is an additional declaration that can be obtained via BeanDefinition’s getAttribute() method if needed.

Resolve the appet-method attribute

Before we analyze the code, let’s take a brief look at the use of appet-method. Its main function is method replacement, which replaces the old method with a new method at run time. Unlike the previous method, this method not only replaces the returned bean, You can dynamically change the original method running logic, demo is as follows:

package com.vipbbo.spring.bean.replacedmethod;
/** * Resolves the appet-method attribute * Its main function is method substitution: that is, replacing old methods with new ones at run time. * Unlike the previous lookup method, this method can not only replace the returned bean, but also dynamically change the original method's running logic *@author paidaxing
 */
public class TestChangeMethod {
    public void changeMe(a)
        {
        System.out.println("ChangeMe"); }}Copy the code

/** * new implementation method */
public class ReplacerChangeMethod implements MethodReplacer {

	@Override
	public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
		System.out.println("I Replace Method");
		return null; }}Copy the code

Configuration file:


      
<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">
<! -- this config file is used to test the resolution of the event-method attribute -->
	<bean id="changeMe" class="com.vipbbo.spring.bean.replacedmethod.TestChangeMethod">
		<replaced-method name="changeMe" replacer="replacer"/>
	</bean>
	<bean id="replacer" class="com.vipbbo.spring.bean.replacedmethod.ReplacerChangeMethod"/>
</beans>
Copy the code

Test method:

public class TestReplacedMethod {
    @Test
        public void testReplaceMethod(a){
        ApplicationContext ac =
                        new ClassPathXmlApplicationContext("replaced-method.xml");
        TestChangeMethod changeMe = (TestChangeMethod) ac.getBean("changeMe"); changeMe.changeMe(); }}Copy the code

So let’s take a look at the method code that resolves appet-methods:

  • See the source code (BeanDefinitionParserDelegate.java)
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); }}}Copy the code

We can see that MethodOverride is constructed in both look-up and event-method, and finally recorded in the attribute of methodOverrides in AbstractBeanDefinition.

Parsing the constructor – arg

Parsing constructors is important and complex,

  • See the source code (BeanDefinitionParserDelegate.java)
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); }}}Copy the code
/** * Parse a constructor-arg element. */
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
    // Extract the index attribute
    String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
    // Extract the type attribute
    String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
    // Extract the name attribute
    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));
                    // Parse the element attributes corresponding to ele
                    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));
                    if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {
                        error("Ambiguous constructor-arg entries for index " + index, ele);
                    } else{ 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(); }}}Copy the code
  • Source code analysis

The above code can be briefly combed, mainly divided into the following steps

  • First, the index, type, name and other attributes are extracted
  • The resolution process varies depending on whether the index attribute is configured

If the index attribute is configured, the resolution process is as follows:

  • The constructor-arg child is read using the parsePropertyValue(ele, bd, null) method
  • Using ConstructorArgumentValues. ValueHolder encapsulation parsing out elements
  • To the index, the type and name attribute is encapsulated into ValueHolder, then add to the current beanDefinition ConstructorArgumentValues IndexedArgumentValue ValueHolder, IndexedArgumentValue is also a map type.

If index holders are not configured, encapsulate the index, Type, and name properties into ValueHolders. And then will be added to the current beanDefinition ValueHolder ConstructorArgumentValues GenericArgumentValue,

Let’s look at the attribute methods that parse the element ele

  • See the source code (BeanDefinitionParserDelegate.java)
@Nullable
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) { String elementName = (propertyName ! =null ?
                    "<property> element for property '" + propertyName + "'" :
                    "<constructor-arg> element");
    // Should only have one child element: ref, value, list, etc.
    NodeList nl = ele.getChildNodes();
    Element subElement = null;
    for (int i = 0; i < nl.getLength(); i++) {
        Node node = nl.item(i);
        // Skip the description and meta attributes
        if (node instanceofElement && ! 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; }}}// Parse the ref attribute
    Boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
    // Parse the value attribute
    Boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
    if((hasRefAttribute && hasValueAttribute) || ((hasRefAttribute || hasValueAttribute) && subElement ! =null)) {
        error(elementName +
                            " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
    }
    if (hasRefAttribute) {
        String refName = ele.getAttribute(REF_ATTRIBUTE);
        if(! StringUtils.hasText(refName)) { error(elementName +" contains empty 'ref' attribute", ele);
        }
        // Use RuntimeBeanReference to encapsulate the Bean corresponding to ref
        RuntimeBeanReference ref = new RuntimeBeanReference(refName);
        ref.setSource(extractSource(ele));
        return ref;
    } else if (hasValueAttribute) {
        // Use TypStringValue to encapsulate the value attribute
        TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
        valueHolder.setSource(extractSource(ele));
        return valueHolder;
    } else if(subElement ! =null) {
        // Parse the child elements
        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; }}Copy the code
  • Source code analysis

For the above code logic for a simple comb

  • I’ll skip the Description and mate attributes first
  • Extract the ref and value attributes on constructor-arg and verify that they exist
  • There is a ref attribute, which encapsulates ref with a RuntimeBeanReference
  • When a value attribute exists, it is encapsulated with TypeStringValue
  • If there are child elements, the method parsePropertySubElement(subElement, BD) is used. For processing, the source code is as follows:
@Nullable
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) {
    return parsePropertySubElement(ele, bd, null);
}
Copy the code
@Nullable
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
    // Check whether it is the default label processing
    if(! isDefaultNamespace(ele)) {return parseNestedCustomElement(ele, bd);
    }
    // Processing of Bean tags
    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;
    }
    // idref element processing
    else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
        return parseIdRefElement(ele);
    }
    // Value element processing
    else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
        return parseValueElement(ele, defaultValueType);
    }
    // NULL element processing
    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 element processing
    else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
        return parseArrayElement(ele, bd);
    }
    // List element processing
    else if (nodeNameEquals(ele, LIST_ELEMENT)) {
        return parseListElement(ele, bd);
    }
    // set element processing
    else if (nodeNameEquals(ele, SET_ELEMENT)) {
        return parseSetElement(ele, bd);
    }
    // Map element processing
    else if (nodeNameEquals(ele, MAP_ELEMENT)) {
        return parseMapElement(ele, bd);
    }
    // props element processing
    else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
        return parsePropsElement(ele);
    } else {
        error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
        return null; }}Copy the code
Parse the Properties child element

Then go back to BeanDefinitionParserDelegate parseBeanDefinitionElement method in class

Look at the parsing of element properties

  • See the source code (BeanDefinitionParserDelegate.java)
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); }}}Copy the code
public void parsePropertyElement(Element ele, BeanDefinition bd) {
    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 {
        // The same attribute cannot be configured more than once
        if (bd.getPropertyValues().contains(propertyName)) {
            error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
            return;
        }
        Object val = parsePropertyValue(ele, bd, propertyName);
        PropertyValue pv = new PropertyValue(propertyName, val);
        parseMetaElements(ele, pv);
        pv.setSource(extractSource(ele));
        bd.getPropertyValues().addPropertyValue(pv);
    }
    finally {
        this.parseState.pop(); }}Copy the code
  • Source code analysis

The logic of the code is very simple: after getting the property of the propertie, encapsulate it with PropertyValue, and then add it to the propertyValueList of the BeanDefinition

Resolve the child element Qualifier

Then go back to BeanDefinitionParserDelegate parseBeanDefinitionElement method in class

Take a look at the parsing of element Qualifier

For Qualifier element acquisition, we are exposed to the form of annotations. When using the Spring framework for automatic injection, the number of matching candidate beans in the Spring container must be one and only one. When no matching Bean is found, The Spring container will throw a BeanCreationException and indicate that you must have at least one matching Bean.

Spring allows us to specify the name of the injected Bean through the Qualifier, so ambiguities are eliminated and the configuration is as follows:

<bean id="myTestBean" class="com.vipbbo.hellospring.MyTestBean">
    <qualifier  type="org.Springframework.beans.factory.annotation.Qualifier" value="gf" />
</bean>
Copy the code

The parsing process is much the same as before and I won’t repeat it here;

At this point, we have converted the XML document to GenericBeanDefinition, that is, all the configurations in XML can be found in the instance class of GenericBeanDefinition.

GenericBeanDefinition is just a subclass implementation, and most of the generality is stored in AbstractBeanDefinition. So again, AbstractBeanDefinition properties to review what configurations we’ve parsed

  • See the source code (AbstractBeanDefinition.java)
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
        implements BeanDefinition.Cloneable {
    // the final modifier.... is omitted
    /** * The scope of the Bean, corresponding to the Bean property scope */
    @Nullable
    private String scope = SCOPE_DEFAULT;
    /** * is abstract, corresponding to the bean attribute abstract */
    private Boolean abstractFlag = false;
    /** * Indicates lazy loading, which corresponds to the bean property lazy-init */
    @Nullable
    private Boolean lazyInit;
    /** * Automatic injection mode, corresponding to the bean property autowire */
    private int autowireMode = AUTOWIRE_NO;
    /** * dependency check, deprecated after Spring3.0 */
    private int dependencyCheck = DEPENDENCY_CHECK_NONE;
    /** * indicates that the instantiation of a bean depends on the first instantiation of another bean, corresponding to the bean attribute depend-on */
    @Nullable
    private String[] dependsOn;
    /** * The autowireCandidate property is set to false so that the Bean will not be considered by the container when looking for an autog, that is, it will not be considered as a candidate for the autog of other beans ** but the Bean itself can still use autog to inject other beans */
    private Boolean autowireCandidate = true;
    /** * Automatic assembly of multiple bean candidates will be preferred, corresponding to the bean property primary */
    private Boolean primary = false;
    /** * Records the Qualifier, corresponding to the child element Qualifier */
    private final Map<String, AutowireCandidateQualifier> qualifiers = new LinkedHashMap<>();
    @Nullable
    privateSupplier<? > instanceSupplier;/** * allows access to non-public constructors and methods, program Settings */
    private Boolean nonPublicAccessAllowed = true;
    /** * Whether to parse the constructor in a loose mode, default true, * if false, if: * interface ITest{} * Class ITestImpl implements ITest(){}; * Class Main{* Main(ITestI) {} * Main(ITestImpl I) {} *} * throws an exception because Spring can't pinpoint which constructor Settings */
    private Boolean lenientConstructorResolution = true;
    /** * corresponds to the bean property factory-bean. Usage:  * 
       * 
       */
    @Nullable
    private String factoryBeanName;
    /** * corresponds to the bean attribute factory-method */
    @Nullable
    private String factoryMethodName;
    /** * Record constructor injection properties: corresponding bean property constructor-arg */
    @Nullable
    private ConstructorArgumentValues constructorArgumentValues;
    /** * Set of common attributes */
    @Nullable
    private MutablePropertyValues propertyValues;
    /** * method override holder, record the lookup-method, replace-method element */
    private MethodOverrides methodOverrides = new MethodOverrides();
    /** * init method, corresponding to bean property init-method */
    @Nullable
    private String initMethodName;
    /** * Destroy method, corresponding to bean property destory-method */
    @Nullable
    private String destroyMethodName;
    /** * whether to execute init-method, program Settings */
    private Boolean enforceInitMethod = true;
    /** * if destory-method is executed, the program is set */
    private Boolean enforceDestroyMethod = true;
    /** * is defined by the user rather than the application itself, set to true when AOP is created, and set to */
    private Boolean synthetic = false;
    /** * Define the bean application: ROLE_APPLICATION: user; ROLE_INFRASTRUCTURE is completely for internal use and has nothing to do with users. ROLE_SUPPORT: part of some complex configuration * program Settings */
    private int role = BeanDefinition.ROLE_APPLICATION;
    /** * The bean description */
    @Nullable
    private String description;
    /** * The resource defined by this bean */
    @Nullable
    private Resource resource;
    // Omit the following...
}
Copy the code

So that’s the end of the BeanDefinition. You can review the process below according to the following table of contents

Sorting is not easy, if it helps you welcome to praise attention

Wechat search [code to meet you] to get more exciting content