Spring decryption-detailed explanation of XML parsing and Bean registration examples, springbean

Source: Internet
Author: User

Spring decryption-detailed explanation of XML parsing and Bean registration examples, springbean

Why do I start to look at spring source code?

It takes almost a year and a half to write code in a single line. I have been using the spring framework since the beginning. Although I will use it, I will build a framework, but I often don't understand the principles behind it. For example: how does spring control transactions, how does spring MVC process requests, and how does aop implement... this makes people feel very unreliable, so start to read and study spring Source Code slowly !!!

How to efficiently view the source code

My answer is to take a look at the source code with specific questions. Otherwise, it is very easy to fall into the details of the source code, and then it will be dizzy. Finally, you will find what you have read for a long time.

Introduction

Spring is an open-source design framework that solves the loose coupling problem between the business logic layer and other layers, and runs the interface-oriented programming idea throughout the system application, it is also one of the essential skills in Java work...

Since the process of Spring source code analysis is recorded, detailed usage will not be described in detail.

Core code

<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version></dependency>

Usage

Public class Application {public static void main (String [] args) {BeanDefinitionRegistry beanFactory = new DefaultListableBeanFactory (); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader (beanFactory ); classPathResource resource = new ClassPathResource ("bean. xml "); // The starting point for loading the entire resource. Reader. loadBeanDefinitions (resource );}}

Decryption

DefaultListableBeanFactory is the default Implementation of Spring registration and bean loading. It can be called the ancestor in the whole Spring Ioc template.

Tracking defalistlistablebeanfactory, you can find the following code block. What is the purpose of this design?

public AbstractAutowireCapableBeanFactory() { super(); ignoreDependencyInterface(BeanNameAware.class); ignoreDependencyInterface(BeanFactoryAware.class); ignoreDependencyInterface(BeanClassLoaderAware.class);}

For example, when property B exists in property A, Spring will automatically instantiate property B if it finds that property B is not instantiated when obtaining property, this is also an important feature provided by Spring. In some cases, B will not be initialized, such as implementing the BeanNameAware interface.

This is introduced in Spring: the specified dependency interfaces are ignored during Automatic Assembly. For example, you can resolve the Application context to register dependencies in other ways, similar to BeanFactory injection through BeanFactoryAware or ApplicationContext injection through ApplicationContextAware.

Resource management

The Resource interface is used to manage resources such as File, URL, and Classpath. The Resource is responsible for reading the configuration File, encapsulating the configuration File as a Resource, and then handing it over to XmlBeanDefinitionReader for processing.

XML Parsing

XmlBeanDefinitionReader is the implementation of Spring resource file reading, parsing, and registration.

Trackingreader.loadBeanDefinitions(resource);, We can see the following core code (excluding comments and throwing exceptions)

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { try { InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { inputStream.close(); } }}

The above code first performs an encoding operation on the Resource, with the aim of worrying about the encoding problem in XML.

Observe carefullyInputSource inputSource = new InputSource(inputStream);The package name is org. xml. sax, so we can conclude that Spring adopts the SAX parsing and InputSource is used to determine how to read the XML file.

Finally, pass the prepared data to the real core processing part through parameters.doLoadBeanDefinitions(inputSource, encodedResource.getResource())

Get Document

1.doLoadBeanDefinitions(inputSource, encodedResource.getResource()); , Omitted catch and comments

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { Document doc = doLoadDocument(inputSource, resource); return registerBeanDefinitions(doc, resource); }}

2.doLoadDocument(inputSource, resource);

protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception { return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, getValidationModeForResource(resource), isNamespaceAware());}

First, you can use getValidationModeForResource to obtain the Authentication Mode (DTD or XSD) of the XML file. You can set the Authentication mode by yourself. By default, VALIDATION_AUTO is enabled to automatically obtain the Authentication mode. The XML file is read through InputStream, check whether it contains the DOCTYPE word. If it contains the DTD, otherwise the XSD is returned.

Common XML file verification modes include:

public class XmlValidationModeDetector { /** * Indicates that DTD validation should be used (we found a "DOCTYPE" declaration). */ public static final int VALIDATION_DTD = 2; /** * Indicates that XSD validation should be used (found no "DOCTYPE" declaration). */ public static final int VALIDATION_XSD = 3; public int detectValidationMode(InputStream inputStream) throws IOException { }}

Inthis.documentLoader.loadDocument The method involves an EntityResolver parameter.

public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {}

What is EntityResolver? Official explanation: If a SAX application needs to implement custom processing of external entities, you must implement this interface and use the setEntityResolver method to register an instance with the SAX drive. That is to say, for parsing an xml file, sax first reads the declaration in the xml file and searches for the corresponding DTD definition according to the declaration to verify the document. By default, it searches for rules, (that is, download the DTD definition through the dtd uri address declared in XML) and perform authentication. The download process is a long process, and when the network is unavailable, an error is reported because the corresponding dtd is not found.

The role of EntityResolver is that the project itself can provide a way to find the DTD Declaration, that is, the process by the program to find the DTD, thus avoiding searching for the corresponding declaration through the network.

3. EntityResolver accepts two parameters:

public abstract InputSource resolveEntity (String publicId,String systemId) throws SAXException, IOException;

3.1 define the bean. xml file. The content is as follows (XSD Mode)

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"></beans>

The following two parameters are parsed:

  • PublicId: null
  • SystemId: http://www.springframework.org/schema/beans/spring-beans.xsd

3.2 define the bean. xml file. The content is as follows (DTD Mode)

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans.dtd"><beans></beans>

The following two parameters are parsed:

  • PublicId:-// SPRING // dtd bean 2.0 // EN
  • SystemId: http://www.springframework.org/dtd/spring-beans.dtd

3.3 Spring uses DelegatingEntityResolver to parse EntityResolver

public class DelegatingEntityResolver { @Override @Nullable public InputSource resolveEntity(String publicId, @Nullable String systemId) throws SAXException, IOException { if (systemId != null) { if (systemId.endsWith(DTD_SUFFIX)) { return this.dtdResolver.resolveEntity(publicId, systemId); } else if (systemId.endsWith(XSD_SUFFIX)) { return this.schemaResolver.resolveEntity(publicId, systemId); } } return null; }}

We can see that different Resolvers are used for different modes.

  • DTD: Using BeansDtdResolver resolution, directly extract the *. dtd (such as: spring-beans.dtd) at the end of systemId, and then go to the current path to find
  • XSD: Uses PluggableSchemaResolver resolution, by default load the XSD file corresponding to systemId under the META-INF/Spring. schemas File

Register Bean

After reading the parsing XML verification, continue to trace the code and see how Spring registers Bean information based on the Document.

Public class XmlBeanDefinitionReader {public int registerBeanDefinitions (Document doc, Resource resource) throws BeanDefinitionStoreException {// create DocumentReader BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader (); // The number of BeanDefinition before record statistics int countBefore = getRegistry (). getBeanDefinitionCount (); // register BeanDefinition documentReader. registerBeanDefinitions (doc, createReaderContext (resource); // record the number of BeanDefinition loaded this time return getRegistry (). getBeanDefinitionCount ()-countBefore ;}}

When registering a Bean, you first use the BeanDefinitionParserDelegate class to determine whether the namespace is the default namespace. The namespace uri is equal to the default uri:

public class BeanDefinitionParserDelegate { public static final String BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans"; public boolean isDefaultNamespace(@Nullable String namespaceUri) { return (!StringUtils.hasLength(namespaceUri) || BEANS_NAMESPACE_URI.equals(namespaceUri)); }}

TrackingdocumentReader.registerBeanDefinitions(doc, createReaderContext(resource)); The doc is converted by loadDocument in the previous code block. The main purpose of this method is to extract the root node (beans)

public class DefaultBeanDefinitionDocumentReader { @Override public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; logger.debug("Loading bean definitions"); Element root = doc.getDocumentElement(); doRegisterBeanDefinitions(root); }}

TrackingdoRegisterBeanDefinitions(root) , We will see the following process

Protected void doRegisterBeanDefinitions (Element root ){//... string profileSpec = root. getAttribute (PROFILE_ATTRIBUTE );//... // empty implement preProcessXml (root); parseBeanDefinitions (root, this. delegate); // empty implement postProcessXml (root); this. delegate = parent ;}

First, parse the profile (a common method is to implement multiple environments with different bean objects initialized by different profiles)

In the next parsing, the template method mode is used. Both preProcessXml and postProcessXml are empty methods, so that the child classes can be processed before and after the parsing. You only need to override these two methods.

Parse and register BeanDefinition. This part of code is relatively simple.

Public class DefaultBeanDefinitionDocumentReader {/*** parse other nodes under the root node import "," alias "," bean ". * @ param root node name */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) ;}} private void parseDefaultElement (Element ele, BeanDefinitionParserDelegate delegate) {if (delegate. nodeNameEquals (ele, IMPORT_ELEMENT) {importBeanDefinitionResource (ele);} else if (delegate. nodeNameEquals (el E, ALIAS_ELEMENT) {processAliasRegistration (ele);} else if (delegate. nodeNameEquals (ele, BEAN_ELEMENT) {processBeanDefinition (ele, delegate);} else if (delegate. nodeNameEquals (ele, NESTED_BEANS_ELEMENT) {// recurse doRegisterBeanDefinitions (ele) ;}/ *** process Bean labels, then register it to the Registry */protected void processBeanDefinition (Element ele, BeanDefinitionParserDelegate delegate) {BeanDefinitionHol Der 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 (new BeanComponentDefinition (bdHolder ));}}}

Delegates The BeanDefinitionParserDelegate class's parseBeanDefinitionElement Method for element parsing and returns the BeanDefinitionHolder type instance bdHolder (including the attributes of the configuration file, such as class, name, id, and alias)
If the returned bdHolder is not empty, if the child node of the default label has a custom attribute, the custom label is parsed again.
After resolution, delegateBeanDefinitionReaderUtils.registerBeanDefinition();Register bdHolder
Send the registration event to inform the listener that the Bean has been successfully registered.

Summary

After a few unknown autumn, winter, and spring, everything will go in the direction you want...

Well, the above is all the content of this article. I hope the content of this article has some reference and learning value for everyone's learning or work. If you have any questions, please leave a message to us, thank you for your support.

Something to say

Full text: https://gitee.com/battcn/battcn-spring-source/tree/master/Chapter1 (local download)

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.