The second Spring source! XML file parsing process

Posted by MastahUK on Mon, 22 Jun 2020 05:05:50 +0200

Spring source code continues to open!

Last article In, brother song shared with you the loading method of configuration files in Spring. If you haven't seen it yet, you must have a look at it first, which will help you better understand this article. Portal: The first part of Spring source code is open and complete! How is the configuration file loaded?.

Last article I shared with you how to load the local configuration file in Spring and how to return the loaded local configuration file through an InputStream. With this in mind, the next step is to parse the InputStream.

In this article, we'll take a look at the whole parsing process.

1.XmlBeanDefinitionReader

stay Last article As you can see, the object loading xml file stream in XmlBeanFactory is XmlBeanDefinitionReader, so we start with XmlBeanDefinitionReader for XML parsing.

Let's take a look at the inheritance diagram of XmlBeanDefinitionReader:

There are several interfaces involved in this inheritance diagram. Let me tell you about them:

  1. BeanDefinitionReader: this interface mainly defines the reading of resource files and converts resources to BeanDefinition.
  2. EnvironmentCapable: this interface defines the method to get the Environment.
  3. AbstractBeanDefinitionReader: implements the methods defined in the BeanDefinitionReader and EnvironmentCapable interfaces. At the same time, AbstractBeanDefinitionReader has a key attribute called ResourceLoader, which can return the corresponding Resource according to the given Resource.
  4. XmlBeanDefinitionReader continues to expand its functions on the basis of AbstractBeanDefinitionReader.

This is the inheritance relationship of XmlBeanDefinitionReader.

After opening the source code of XmlBeanDefinitionReader, we found that there are two key objects:

  • BeanDefinitionDocumentReader: the only implementation class of BeanDefinitionDocumentReader interface is DefaultBeanDefinitionDocumentReader. Here, the read of Document object is defined and the read property is converted to BeanDefinition.
  • DocumentLoader: converts a resource file to a Document object.

>I'm afraid that some of my friends may not know what document is. I'd like to say a few more words here. Document is the document object obtained during XML parsing. The document object represents the model tree of an XML document. All other nodes are included in the document object in a certain order and arranged into a tree structure. In the future, all operations on the XML document are independent of the parser, and can be performed directly on the document object. The main XML parsing methods are SAX parsing, DOM parsing and Pull parsing. If you are not familiar with XML file parsing, you can review it by yourself. Brother song is no longer verbose here.

OK, after understanding the inheritance relationship of XmlBeanDefinitionReader and the two key classes defined in it, let's sort out the functions of XmlBeanDefinitionReader

  1. First, XmlBeanDefinitionReader inherits from AbstractBeanDefinitionReader, and uses the ResourceLoader in AbstractBeanDefinitionReader to convert the configuration file path to the corresponding Resource.
  2. Next, use DocumentLoader to turn Resource into Document.
  3. Finally, the bean definition Document reader is used to parse the Document.

After making these clear, let's go through the process.

2. Process

I don't know if I still remember a simple case given by song Ge in the last article:

public static void main(String[] args) {
    XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
    User user = factory.getBean(User.class);
    System.out.println("user = " + user);
}

Let's follow the construction method of XmlBeanFactory.

Let's first look at the construction method of XmlBeanFactory:

public class XmlBeanFactory extends DefaultListableBeanFactory {
	private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
	public XmlBeanFactory(Resource resource) throws BeansException {
		this(resource, null);
	}
	public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
		super(parentBeanFactory);
		this.reader.loadBeanDefinitions(resource);
	}

}

The source code of XmlBeanFactory is very simple. In fact, its main functions are implemented in DefaultListableBeanFactory. Brother song will write a special article to introduce DefaultListableBeanFactory later. We will not expand it too much here.

XmlBeanDefinitionReader is defined in XmlBeanFactory to read resources. By default, parentBeanFactory is null. The specific read operation is provided by XmlBeanDefinitionReader ා loadbeandefinitions method. Let's look at this method:

@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
	return loadBeanDefinitions(new EncodedResource(resource));
}
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
	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());
		}
		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();
		}
	}
}
  1. In the loadBeanDefinitions method, the incoming Resource will be first transformed into an encoded Resource, that is, the encoding of the incoming Resource. The so-called encoding should not be too complicated. In fact, it is to add a parameter in the encoding format when reading the Resource in the future. For details, please refer to the EncodedResource getReader Method, because it's relatively simple, I won't post it here.
  2. Continue to call another overloaded loadBeanDefinitions method, passing in the encoded resource.
  3. First, add the current resource to a ThreadLocal to avoid repeated loading.
  4. I / O of XML configuration file is transferred to an InputSource object. InputSource is the starting point of XML file parsing. When you review the XML file parsing, SongGe will not introduce it too much.
  5. If the resource has an encoding format, set the encoding format to the inputSource object.
  6. Call the doLoadBeanDefinitions method for further parsing.
  7. Finally, remove the resource from the collection.

In step 6 above, the doLoadBeanDefinitions method is called. What this method does is to parse the resource file into a Document object, as follows:

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
		throws BeanDefinitionStoreException {
	try {
		Document doc = doLoadDocument(inputSource, resource);
		int count = registerBeanDefinitions(doc, 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);
	}
}

As you can see, here is to call doLoadDocument for resource resolution, and finally get a Document object.

Let's look at the doLoadDocument method:

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

As you can see, the final call here is the documentLoader ා loaddocument method. The documentLoader is the DefaultDocumentLoader object introduced by SongGe in the first section.

There are five parameters needed to call this method:

  1. Needless to say, the first InputSource is the resource file to be called.
  2. The second EntityResolver mainly deals with file validation.
  3. The third ErrorHandler is an error handler.
  4. The fourth validation mode refers to the validation mode of the XML file.
  5. The fifth namespace aware indicates whether to turn on auto aware namespaces.

The specific calls are as follows:

public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
		ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
	DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
	if (logger.isTraceEnabled()) {
		logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
	}
	DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
	return builder.parse(inputSource);
}

I won't explain it too much here. It's basically in the field of XML parsing. Let's review the XML parsing methods in Java.

3. Summary

In this paper, song Ge mainly introduces how to get a Document object and a Document object in Spring. Next, we can parse the Document object and get various properties to define bean definition.

But if you have never studied the Spring source code, I believe there may be many doubts in this article, such as the use of EntityResolver? What is ValidationMode? So don't worry, boys. Brother song will introduce these things one by one in the next article.

Well, let's talk about it today. If you think it's rewarding, please remember to watch and encourage brother song

Topics: Programming xml Spring encoding Attribute