The basic implementation of the container of "Spring source code deep parsing Hao Jia version 2" and the loading of XML files

catalogue

  1. The overall architecture of Spring
  2. The basic implementation of container and the loading of XML file

1, The overall architecture of Spring

Spring is a layered architecture, which mainly includes the following parts

  1. Core Container
  2. Data Access
  3. Web
  4. Aop
  5. Test

1,Core Container

Core container, including core, Beans, Context and Expression Language(EL expression) modules. Core and Beans are the basic parts, providing IoC (control inversion) and DI (dependency injection), providing the classic implementation of factory mode to eliminate the need for program singleton mode, and decoupling through xml configuration and code

  • Core: contains the basic core tool class, which is needed by other components. It can be regarded as Utils tool class
  • Beans: includes accessing configuration files, creating and managing bean s, and performing DI
  • Context: it is mainly the further encapsulation of Core and Beans, inheriting the characteristics of Beans and providing a lot of expansion, such as internationalization, event propagation, resource loading, etc. The ApplicationContext interface is the key to the context module
  • EL: used to query and manipulate objects at run time

2,Data Access

Related to data access, including JDBC, ORM, OXM, JMS and Transaction modules

  • JDBC: provides an abstraction layer for different database connection access
  • ORM: provides an interaction layer for popular object relational mapping API s such as JPA, JDO, Hibernate, iBatis, etc
  • JMS: contains some features of manufacturing and consuming messages

3,Web

4,AOP

5,Test

2, Basic implementation of container

Because Spring can use xml to complete the related configuration of containers and beans, let's first look at the most basic instance of obtaining XmlBeanFactory type

// XmlBeanFactory inherits BeanFactory 
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("beanFactory.xml"));

bean is the most core thing in Spring, and the two core classes under the beans package

  1. DefaultListableBeanFactory: the core part of bean loading, which is the default implementation of bean registration and bean loading
  2. XmlBeanDefinitionReader: reading of xml configuration file

1. Two classes of core

Inheritance system under BeanFactory interface in Spring version 5.2.12

DefaultListableBeanFactory


XmlBeanFactory implements DefaultListableBeanFactory and adds the member variable reader of XmlBeanDefinitionReader type on this basis

Used to read xml configuration files

XmlBeanDefinitionReader

1. Let's first look at the construction of new ClassPathResource("beanFactory.xml"). The getInputStream() method of ClassPathResource uses getResourceAsStream(path) of ClassLoader to obtain InputStream and then convert it into Resource

// package org.springframework.core.io;
// public class ClassPathResource extends AbstractFileResolvingResource 

public InputStream getInputStream() throws IOException {
        InputStream is;
        if (this.clazz != null) {
            is = this.clazz.getResourceAsStream(this.path);
        } else if (this.classLoader != null) {
            is = this.classLoader.getResourceAsStream(this.path);
        } else {
            is = ClassLoader.getSystemResourceAsStream(this.path);
        }

        if (is == null) {
            throw new FileNotFoundException(this.getDescription() + " cannot be opened because it does not exist");
        } else {
            return is;
        }
    }

2. Detailed instantiation process of XmlBeanFactory

1. Return to the construction method of XmlBeanFactory new XmlBeanFactory(new ClassPathResource("beanFactory.xml");

/** @deprecated */
// Because it uses SpringBoot source code analysis, it has been marked out of date
@Deprecated
public class XmlBeanFactory extends DefaultListableBeanFactory {
    private final XmlBeanDefinitionReader reader;

    public XmlBeanFactory(Resource resource) throws BeansException {
        this(resource, (BeanFactory)null);
    }

    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
    	// 1. Call the constructor of the parent class AbstractAutowireCapableBeanFactory
        super(parentBeanFactory);
        // 2. Create reader instance
        this.reader = new XmlBeanDefinitionReader(this);
        // 3. Start parsing Resource
        this.reader.loadBeanDefinitions(resource);
    }
}

1.1. Call the construction method of the parent class AbstractAutowireCapableBeanFactory

 public AbstractAutowireCapableBeanFactory() {
        this.instantiationStrategy = new CglibSubclassingInstantiationStrategy();
        this.parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
        this.allowCircularReferences = true;
        this.allowRawInjectionDespiteWrapping = false;
        this.ignoredDependencyTypes = new HashSet();
        this.ignoredDependencyInterfaces = new HashSet();
        this.currentlyCreatedBean = new NamedThreadLocal("Currently created bean");
        this.factoryBeanInstanceCache = new ConcurrentHashMap();
        this.factoryMethodCandidateCache = new ConcurrentHashMap();
        this.filteredPropertyDescriptorsCache = new ConcurrentHashMap();
        
        // Ignoring the automatic transfer of a given interface, Bean, BeanFactory and BeanClassloader can inject corresponding beans when implementing the following three interfaces
        //When the properties of other beans contain the beans in the above case, the beans in the above case will not be automatically initialized due to dependency injection
        this.ignoreDependencyInterface(BeanNameAware.class);
        this.ignoreDependencyInterface(BeanFactoryAware.class);
        this.ignoreDependencyInterface(BeanClassLoaderAware.class);
    }

1.2. Create a reader instance of XmlBeanDefinitionReader type

1.3 the key logic is this reader. loadBeanDefinitions(resource);

Dbload definitions method

Before entering the XmlBeanDefinitionReader, prepare to load the XML document and parse the Bean

  1. Encapsulate the Resource file: first, encapsulate the parameter Resource with the EncodedResource class
  2. Get input stream: get the corresponding InputStream from the Resource and encapsulate it as InputSource
  3. Call doLoadBeanDefinitions(inputSource, encodedResource.getResource()) through the constructed Resource and InputSource;

The doLoadBeanDefinitions method mainly does three things

  1. Get the validation mode of XML file: XML file mainly includes DTD and XSD modes, getValidationMode(resource) - > detectvalidationmode (resource) - > xmlvalidationmodedetector #validationmodedetector (resource) method
  2. Load the XML file and get the corresponding Document: DefaultDocumentLoader, the implementation subclass entrusted to DoucumentLoader
  3. Register the Bean information according to the returned Document: the implementation subclass DefaultBeanDefinitionDocumentReader entrusted to BeanDefinitionDocumentReader
doLoadBeanDefinitions method
// XmlBeanDefinitionReader#doLoadBeanDefinitions()
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
        try {
        	// 1. Get Document
            Document doc = this.doLoadDocument(inputSource, resource);
            // 2. Register BeanDefinitions by calling registerBeanDefinitions
            int count = this.registerBeanDefinitions(doc, resource);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Loaded " + count + " bean definitions from " + resource);
            }

            return count;
        } catch (BeanDefinitionStoreException var5) {
            throw var5;
        } catch (SAXParseException var6) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + var6.getLineNumber() + " in XML document from " + resource + " is invalid", var6);
        } catch (SAXException var7) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", var7);
        } catch (ParserConfigurationException var8) {
            throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, var8);
        } catch (IOException var9) {
            throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, var9);
        } catch (Throwable var10) {
            throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, var10);
        }
    }


//1. Get Document

// 1. Delegate DocumentLoader to transform Document. The actual implementation subclass is DefaultDocumentLoader

// DefaultDocumentLoader#loadDocument()

//EntityResolver function: provide a local method to find DTD to avoid network search. Mainly for verification
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
        DocumentBuilderFactory factory = this.createDocumentBuilderFactory(validationMode, namespaceAware);
        if (logger.isTraceEnabled()) {
            logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
        }

        DocumentBuilder builder = this.createDocumentBuilder(factory, entityResolver, errorHandler);
        return builder.parse(inputSource);
    }


//2. Call registerBeanDefinitions to register BeanDefinitions

// 2. BeanDefinitionDocumentReader registers BeanDefinitions with the Document


public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
        int countBefore = this.getRegistry().getBeanDefinitionCount();
       
        documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
        return this.getRegistry().getBeanDefinitionCount() - countBefore;
    }
    
 // DefaultBeanDefinitionDocumentReader#registerBeanDefinitions() is actually called
 public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        this.doRegisterBeanDefinitions(doc.getDocumentElement());
    }

protected void doRegisterBeanDefinitions(Element root) {
		// Specialized processing and parsing
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
        if (this.delegate.isDefaultNamespace(root)) {
        // Processing the profile attribute, such as an xml, can set and activate the configuration environment such as dev and prod
            String profileSpec = root.getAttribute("profile");
            if (StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
                if (!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if (this.logger.isDebugEnabled()) {
                        this.logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + this.getReaderContext().getResource());
                    }

                    return;
                }
            }
        }
		// The pre-processing of parsing is left to the subclass implementation
        this.preProcessXml(root);
        // analysis
        this.parseBeanDefinitions(root, this.delegate);
        // After parsing, leave it to subclass implementation
        this.postProcessXml(root);
        this.delegate = parent;
    }


//3. Parse parseBeanDefinitions(root, this.delegate);

 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
 // Processing of beans
        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)) {
                    // Processing of beans: tag and sub tag are the default namespace, such as < bean >
                        this.parseDefaultElement(ele, delegate);
                    } else {
                    // Processing of bean s: label resolution of custom namespaces, such as < TX: annotation driven / >
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        } else {
            delegate.parseCustomElement(root);
        }

    }

The next part: parsing XML file tags

Keywords: Spring xml Container

Added by zTagged on Mon, 28 Feb 2022 16:39:47 +0200