catalogue
- The overall architecture of Spring
- 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
- Core Container
- Data Access
- Web
- Aop
- 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
- DefaultListableBeanFactory: the core part of bean loading, which is the default implementation of bean registration and bean loading
- 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
- Encapsulate the Resource file: first, encapsulate the parameter Resource with the EncodedResource class
- Get input stream: get the corresponding InputStream from the Resource and encapsulate it as InputSource
- Call doLoadBeanDefinitions(inputSource, encodedResource.getResource()) through the constructed Resource and InputSource;
The doLoadBeanDefinitions method mainly does three things
- Get the validation mode of XML file: XML file mainly includes DTD and XSD modes, getValidationMode(resource) - > detectvalidationmode (resource) - > xmlvalidationmodedetector #validationmodedetector (resource) method
- Load the XML file and get the corresponding Document: DefaultDocumentLoader, the implementation subclass entrusted to DoucumentLoader
- 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