preface

Previously, we combed through the whole process of Spring container initialization. Today, we will talk about one of the key points of the initialization process, the parsing of Spring configuration and the loading of BeanDefinition.

Although Spring configuration comes in many forms, it is no longer limited to XML files. However, this article uses the spring-Demo project from the previous article as an example to analyze how the configuration file spring-beans.xml for XML configuration is parsed. Once you’ve mastered the XML configuration form, you can look at other ways to do it by analogy.

The original demo expansion

There was a little bit of content in the demo, so let’s enrich it and then we’ll analyze it. Project address: Spring-XML-Demo-enhanced

Project structure

coding

Component, Component empty class

package cn.dingyufan.learnspring.springxmldemoenhanced.component;

import org.springframework.stereotype.Component;

@Component
public class OneComponent {}Copy the code
package cn.dingyufan.learnspring.springxmldemoenhanced.component;

import org.springframework.stereotype.Component;

@Component
public class TwoComponent {}Copy the code

HelloController adds oneComponent injection

package cn.dingyufan.learnspring.springxmldemoenhanced.controller;

import cn.dingyufan.learnspring.springxmldemoenhanced.component.OneComponent;
import cn.dingyufan.learnspring.springxmldemoenhanced.service.HelloService;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Autowired
    private HelloService helloService;

    @Autowired
    private OneComponent oneComponent;

    // The property is automatically printed after injection for easy validation
    @PostConstruct
    private void init(a) {
        System.out.println("HelloController.init(),helloService=>" + helloService);
        System.out.println("HelloController.init(),oneComponent=>" + oneComponent);
    }

    @GetMapping("/hello")
    public ResponseEntity<String> hello(HttpServletRequest request) {
        return ResponseEntity.ok("Hello Spring!");
    }

    @GetMapping("/check")
    public ResponseEntity<String> check(HttpServletRequest request) {
        return ResponseEntity.ok(String.valueOf(helloService.hashCode()));
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("HelloController injects applicationContext =>" + applicationContext.getDisplayName());
        this.applicationContext = applicationContext; }}Copy the code

No change HelloService

package cn.dingyufan.learnspring.springxmldemoenhanced.service;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;


public class HelloService implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("HelloService injections applicationContext =>" + applicationContext.getDisplayName());
        this.applicationContext = applicationContext; }}Copy the code

WorldService injects multiple contents, an object, a number, and an array. It’s all configured through XML configuration and then injected by setter methods. The init() method is in the XML configuration of the init-method item, which is called in the order after the bean property is injected, so we use this method to check if the content is injected.

package cn.dingyufan.learnspring.springxmldemoenhanced.service;

import java.util.List;

public class WorldService {
    private HelloService helloService;
    private Integer year;
    private List<String> ultraman;

    // The 
      
        init-method attribute is configured in XML
      
    private void init(a) {
        System.out.println("WorldService.init(),helloService=>" + helloService);
        System.out.println("WorldService.init(),year=>" + year);
        System.out.println("WorldService.init(),ultraman=>" + ultraman);
    }

    public void setHelloService(HelloService helloService) {
        this.helloService = helloService;
    }

    public void setUltraman(List<String> ultraman) {
        this.ultraman = ultraman;
    }

    public void setYear(Integer year) {
        this.year = year; }}Copy the code

configuration

For configuration, some common configuration tags are used in Demo.

First you can see a new spring-component.xml.


      
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <! -- Scan and register components under base-package -->
    <context:component-scan base-package="cn.dingyufan.learnspring.springxmldemoenhanced.component"/>

</beans>
Copy the code

The spring-beans. XML file adds the worldService definition and configates attributes for worldService. Added the <import> tag to import the contents of spring-component. XML into spring-beans.xml. The <import> tag makes it easier to split the managed beans and avoid a configuration file that is too large.


      
<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">

    <bean id="helloService" class="cn.dingyufan.learnspring.springxmldemoenhanced.service.HelloService"/>

    <bean id="worldService" class="cn.dingyufan.learnspring.springxmldemoenhanced.service.WorldService" init-method="init">
        <property name="helloService" ref="helloService"/>
        <property name="year" value="2021"/>
        <property name="ultraman">
            <list>
                <value>Robot --</value>
                <value>The severn</value>
                <value>ace</value>
            </list>
        </property>
    </bean>

    <import resource="spring-component.xml"/>
</beans>
Copy the code

The spring-mvC.xml, web. XML, and pom.xml are unchanged and will not be described here, if necessary

run

As you can see from the console print, the objects, numbers, and arrays in HelloService are all successfully injected; The new oneComponent added to HelloController has also been successfully injected. It means our configuration has taken effect.

The source code parsing

From the above, configuration files are loaded during the beanFactory refresh process. We’ll still use XmlWebApplicationContext as an example, starting with the obtainFreshBeanFactory() method.

AbstractApplicationContext

The XmlWebApplicationContext obtainFreshBeanFactory () method is its parent class AbstractApplicationContext implementation

    // AbstractApplicationContext.java
    protected ConfigurableListableBeanFactory obtainFreshBeanFactory(a) {
        / / short method
        refreshBeanFactory();
        return getBeanFactory();
    }
Copy the code

AbstractRefreshableApplicationContext

The XmlWebApplicationContext refreshBeanFactory () method is the parent class AbstractRefreshableApplicationContext implementation.

    // AbstractRefreshableApplicationContext.java
    @Override
    protected final void refreshBeanFactory(a) throws BeansException {
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            beanFactory.setSerializationId(getId());
            customizeBeanFactory(beanFactory);
            // Empty method to parse configuration load BeanDefinition
            loadBeanDefinitions(beanFactory);
            this.beanFactory = beanFactory;
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for "+ getDisplayName(), ex); }}Copy the code

XmlWebApplicationContext

At this point, the loadBeanDefinitions(beanFactory) method is finally implemented by XmlWebApplicationContext itself.

    // XmlWebApplicationContext.java
    @Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // Create an XmlBeanDefinitionReader for BeanFactory
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        // Add configuration for XmlBeanDefinitionReader
        beanDefinitionReader.setEnvironment(getEnvironment());
        // Set XmlWebApplicationContext itself to ResourceLoader
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

        // Empty method to add the desired configuration to the subclass. Template method pattern again.
        initBeanDefinitionReader(beanDefinitionReader);
        / / load BeanDefinition
        loadBeanDefinitions(beanDefinitionReader);
    }
Copy the code

As you can see, XmlBeanDefinitionReader is prepared for parsing configurations, leaving room for custom configurations for subclasses, and then actually taking a different loadBeanDefinitions(Reader) method in another.

    // XmlWebApplicationContext.java
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
        String[] configLocations = getConfigLocations();
        if(configLocations ! =null) {
            for(String configLocation : configLocations) { reader.loadBeanDefinitions(configLocation); }}}Copy the code

As you can see from the code, the loadBeanDefinitions(Reader) method simply iterates through our XML configuration file path and then hands it over to XmlBeanDefinitionReader.

By the way, do you remember where this configLocations came from? It is context-param that we configure in web.xml and then get from servletContext and assign to applicationContext in ContextLoader.

AbstractBeanDefinitionReader

So XmlBeanDefinitionReader once we get the configuration file path, what does it do

  • Get ResourceLoader
  • Load the configuration file as Resource using ResourceLoader
  • Iterate over the loaded Resource and call the subclass method to load the BeanDefinition
    / / AbstractBeanDefinitionReader. Java. Is XmlBeanDefinitionReader parent class
    @Override
    public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
            return loadBeanDefinitions(location, null);
    }
    
    
    public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
        // This ResourceLoader is set earlier in the loadBeanDefinitions(beanFactory) method of the XmlWebApplicationContext class
        ResourceLoader resourceLoader = getResourceLoader();
        if (resourceLoader == null) {
            throw new BeanDefinitionStoreException("Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
        }
        
        XmlWebApplicationContext implements the ResourcePatternResolver interface
        if (resourceLoader instanceof ResourcePatternResolver) {
            try {
                // Load the configuration file as Resource using ResourceLoader
                Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
                int count = loadBeanDefinitions(resources);
                if(actualResources ! =null) {
                    Collections.addAll(actualResources, resources);
                }
                if (logger.isTraceEnabled()) {
                    logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
                }
                return count;
            }
            catch (IOException ex) {
                throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern [" + location + "]", ex); }}else {
            Resource resource = resourceLoader.getResource(location);
            int count = loadBeanDefinitions(resource);
            if(actualResources ! =null) {
                actualResources.add(resource);
            }
            if (logger.isTraceEnabled()) {
                logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
            }
            returncount; }}@Override
    public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
        Assert.notNull(resources, "Resource array must not be null");
        int count = 0;
        for (Resource resource : resources) {
            // loadBeanDefinitions(Resource) is a subclass implementation
            // This is the loadBeanDefinitions(Resource) method in XmlWebApplicationContext
            count += loadBeanDefinitions(resource);
        }
        return count;
    }

Copy the code

The first thing you can see is that it gets a ResourceLoader using the getResourceLoader() method, This ResourceLoader is the loadBeanDefinitions(beanFactory) method of the XmlWebApplicationContext class. We’re going to set it, and we’re going to set it to the XmlWebApplicationContext object itself. You can scroll up a little bit.

Then determine if it is the ResourcePatternResolver implementation class. XmlWebApplicationContext implements ApplicationContext, and the ApplicationContext interface inherits directly from the ResourcePatternResolver interface.

public interface ApplicationContext extends EnvironmentCapable.ListableBeanFactory.HierarchicalBeanFactory.MessageSource.ApplicationEventPublisher.ResourcePatternResolver {

	@Nullable
	String getId(a);

	String getApplicationName(a);

	String getDisplayName(a);

	long getStartupDate(a);

	@Nullable
	ApplicationContext getParent(a);

	AutowireCapableBeanFactory getAutowireCapableBeanFactory(a) throws IllegalStateException;

}
Copy the code

If we search ResourcePatternResolver’s implementation class, we can also clearly see the XmlWebApplicationContext.

Then load the configuration file as Resource. The load is dependent on the XmlWebApplicationContext parent AbstractApplicationContext ResourcePatternResolver types ResourcePatternResolver member variables. AbstractApplicationContext constructor, will create a variable instance, is a PathMatchingResourcePatternResolver. I’m not going to talk about the process, but let’s figure out which class does this.

    // AbstractApplicationContext.java
    public AbstractApplicationContext(a) {
        this.resourcePatternResolver = getResourcePatternResolver();
    }
    
    protected ResourcePatternResolver getResourcePatternResolver(a) {
        return new PathMatchingResourcePatternResolver(this);
    }
Copy the code

XmlBeanDefinitionReader

The last AbstractBeanDefinitionReader call subclasses XmlBeanDefinitionReader method resolving the Resource. Going back to the XmlWebApplicationContext class, it wraps Resource as EncodedResource; Then read the file with IO stream, convert the XML file into the corresponding Document object, convenient program processing. Then instantiates a BeanDefinitionDocumentReader to read the Document object.

    // XmlBeanDefinitionReader.java
    @Override
    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        return loadBeanDefinitions(new EncodedResource(resource));
    }

    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if (logger.isTraceEnabled()) {
            logger.trace("Loading XML bean definitions from " + encodedResource);
        }
        
        // Currently loaded Resource
        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();

        if(! currentResources.add(encodedResource)) {throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        }

        try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
            InputSource inputSource = new InputSource(inputStream);
            if(encodedResource.getEncoding() ! =null) {
                inputSource.setEncoding(encodedResource.getEncoding());
            }
            // inputStream reads Resource and continues
            return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), ex);
        }
        finally {
            currentResources.remove(encodedResource);
            if (currentResources.isEmpty()) {
                this.resourcesCurrentlyBeingLoaded.remove(); }}}protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
        try {
            // Parse the XML to form the corresponding Document object
            Document doc = doLoadDocument(inputSource, resource);
            int count = registerBeanDefinitions(doc, resource);
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + count + " bean definitions from " + resource);
            }
            return count;
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (SAXParseException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
        }
        catch (SAXException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", ex);
        }
        catch (ParserConfigurationException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, ex);
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, ex);
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from "+ resource, ex); }}public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        int countBefore = getRegistry().getBeanDefinitionCount();
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }
Copy the code

DefaultBeanDefinitionDocumentReader

Read the Document object the default is to use DefaultBeanDefinitionDocumentReader, in method and to create a BeanDefinitionParserDelegate proxy class. This method involves the processing of namespace and profile, so I will not go into details at present. There’s nothing to discuss about the two empty methods; Focus on parseBeanDefinitions(root, this.delegate).

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

    protected void doRegisterBeanDefinitions(Element root) {
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);

        if (this.delegate.isDefaultNamespace(root)) {
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            // Check whether a profile is configured.
            if (StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                // We cannot use Profiles.of(...) since profile expressions are not supported
                // in XML config. See SPR-12458 for details.
                if(! getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {if (logger.isDebugEnabled()) {
                        logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource());
                    }
                    return;
                }
            }
        }

        preProcessXml(root);
        parseBeanDefinitions(root, this.delegate);
        postProcessXml(root);

        this.delegate = parent;
    }

Copy the code

In this method, there are two cases, one is the default namespace, the other is actually a custom namespace (custom tag). The application of custom tags is also very widespread in Spring applications. For example, well-known frameworks such as SpringMVC and Dubbo all use custom tags. We will only look at the default namespace handling here, and the other one will be covered separately.

    // DefaultBeanDefinitionDocumentReader.java
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        if (delegate.isDefaultNamespace(root)) {
            NodeList nl = root.getChildNodes();
            for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                if (node instanceof Element) {
                    Element ele = (Element) node;
                    if (delegate.isDefaultNamespace(ele)) {
                        parseDefaultElement(ele, delegate);
                    }
                    else{ delegate.parseCustomElement(ele); }}}}else{ delegate.parseCustomElement(root); }}Copy the code

For nodes with a default namespace, the final method is parseDefaultElement(ele, delegate). The four IFs in this method parse different tags. Tag parsing is much the same, let’s take

parsing as an example.

    // DefaultBeanDefinitionDocumentReader.java
    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)) {
            // recursedoRegisterBeanDefinitions(ele); }}Copy the code

Going into the processBeanDefinition(ele, delegate) method we can see that the following method does four things

  • Resolve the node to BeanDefinitionHolder with the resolution Delegate proxy object
  • If a Decorator is defined, the BeanDefinition is decorated
  • Register the final BeanDefinition
  • Send the bean registration event

In fact, the first step is the most important, and we will see how the proxy object that is responsible for parsing is handled in a moment. Use a Decorator. What does this do? A Decorator is registered in the NamespaceHandler to handle the node’s namespaceUri. More on that later.

    // DefaultBeanDefinitionDocumentReader.java
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        // Parse the node, creating BeanDefinitionHolder
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if(bdHolder ! =null) {
            // If a decorator is defined, the BeanDefinition is decorated.
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // Register the final BeanDefinition
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex);
            }
            // Send the bean registration event
            getReaderContext().fireComponentRegistered(newBeanComponentDefinition(bdHolder)); }}Copy the code

BeanDefinitionParserDelegate

Let’s focus on BeanDefinitionHolder. The main logic for delegate parsing is as follows

  • Read node id, name
  • Take beanName and check if it is unique
  • To parse AbstractBeanDefinition
    • Create an AbstractBeanDefinition based on the className, parent type, and ClassLoader
    • Read various properties and populate them with AbstractBeanDefinition
  • Try beanName again
  • Assemble BeanDefinitionHolder
    // BeanDefinitionParserDelegate.java
    @Nullable
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
            return parseBeanDefinitionElement(ele, null);
    }
    
    @Nullable
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
        // Read node id, name
        String id = ele.getAttribute(ID_ATTRIBUTE);
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

        List<String> aliases = new ArrayList<>();
        if (StringUtils.hasLength(nameAttr)) {
            String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            aliases.addAll(Arrays.asList(nameArr));
        }
        
        // beanName defaults to id. If id is not set to name, select the first name as id
        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) {
            // Check if beanName is repeated
            checkNameUniqueness(beanName, aliases, ele);
        }
        
        // Parse the node to AbstractBeanDefinition
        AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
        if(beanDefinition ! =null) {
            if(! StringUtils.hasText(beanName)) {If there is no beanName, generate a beanName
                try {
                    if(containingBean ! =null) {
                        beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);
                    }
                    else {
                        beanName = this.readerContext.generateBeanName(beanDefinition);
                        String beanClassName = beanDefinition.getBeanClassName();
                        if(beanClassName ! =null 
                                && beanName.startsWith(beanClassName) 
                                && beanName.length() > beanClassName.length() 
                                &&!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { aliases.add(beanClassName); }}if (logger.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);
            // AbstractBeanDefinitionHolder, beanName, aliases assemble BeanDefinitionHolder
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        }

        return null;
    }
    
    
    @Nullable
    public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean) {

        this.parseState.push(new BeanEntry(beanName));
        
        // Read the class attribute in node
        String className = null;
        if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
            className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
        }
        // Read the parent property in node
        String parent = null;
        if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
            parent = ele.getAttribute(PARENT_ATTRIBUTE);
        }

        try {
            // Create an AbstractBeanDefinition based on className, parent type, and ClassLoader
            AbstractBeanDefinition bd = createBeanDefinition(className, parent);
            // Read various attributes and populate them with AbstractBeanDefinition. Such as:
            // Singleton, scope, abstract, lazy-init, autowire, Depends -on, autowire-candidate,
            // primary, init-method, destroy-method, factory-method, factory-bean
            parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
            // Resolve the descriptionb tag
            bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
            
            // Parse meta tags
            parseMetaElements(ele, bd);
            // parse the lookup-method tag
            parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
            // Resolve the event-method tag
            parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

            // Parse the constructor-arg tag
            parseConstructorArgElements(ele, bd);
            // Parse the property tag
            parsePropertyElements(ele, bd);
            // Resolve the Qualifier tag
            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

conclusion

It’s a long paragraph, maybe it’s a little bit clearer to have a picture of the process.