[toc]


As you can see from the previous note, a lot of parsing is done when the container registers the bean information, and the XML file contains many tags and attributes, such as bean, import tags, meta, look-up, and replace child attributes.

The previous article focused on the infrastructure of the Spring container and didn’t go into detail about how these tags are parsed.

So this article is to fill the hole, introduce these tags in the code how to identify and parse ~

The structure of this note is as follows:

  • Introduces the concept of
  • showdemoCode, how to use
  • Combine source code analysis
  • Talk and think

Again, download the project to see the full comments and follow the source code to analyze ~

Code cloud Gitee address

Making the address


In Spring, there are two types of tags, default and custom:

  • This is the default tag. There are four other tags (Import, Alias, bean, beans)

  • Custom tags The purpose of custom tags is to provide configurable support for the system. For example, the transaction tag < TX: Annotation-driven /> is a Spring custom tag. It inherits NamespaceHandler to resolve custom namespaces.

Take a look at how the source code distinguishes the two:

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
	if (delegate.isDefaultNamespace(root)) {
		// Comment 1.12 Traversing the node list in doc
		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)) {
					// Note 1.13 bean registry that recognizes default tags
					// Depending on the element name, call a different loading method to register the bean
					parseDefaultElement(ele, delegate);
				}
				else{ delegate.parseCustomElement(ele); }}}}else{ delegate.parseCustomElement(root); }}Copy the code

As you can see, in the code, the key method is to delegate. IsDefaultNamespace (ele) to judge, identify the scanning to the element belongs to what kind of label.

Find namespace NamespaceURI variables, if is http://www.springframework.org/schema/beans, says it is the default label, and then made the default label element analysis, or use the custom tag.

This note mainly records the default tag parsing, down to the formal introduction ~


Default tag parsing

ParseDefaultElement method is used to resolve the default label, tracking down, found that the four kinds of labels do the different treatment, including bean label parsing is the most difficult (compared to other three), so we will bean analytical read label, other three tags analysis also can be better familiar with.

Entry method:

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
	// Comment 2.1 Default tag parsing
	if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
		// Parse the import tag
		importBeanDefinitionResource(ele);
	}
	else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
		// Parse the alias tag
		processAliasRegistration(ele);
	}
	else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
		// A method to parse bean tags
		processBeanDefinition(ele, delegate);
	}
	else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
		// recurse
		// Parse beans tags recursively, re-parsing tags under this elementdoRegisterBeanDefinitions(ele); }}Copy the code

Bean label parsing entry

Navigate to the third method above: processBeanDefinition(ele, delegate) :

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		// Comment 1.15 Parsing elements of bean names
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if(bdHolder ! =null) {
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
				// Register the final decorated instance. (Note 1.16 Register the last 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. Notifies associated listeners that the bean has been loaded
			getReaderContext().fireComponentRegistered(newBeanComponentDefinition(bdHolder)); }}Copy the code

The last note briefly described the functionality of this method: map attributes configured in XML to the Document object, and then register them. Here’s a complete description of how this method works:

  • Create an instancebdHolder: First delegateBeanDefinitionParserDelegateOf the classparseBeanDefinitionElementMethod to parse elements. After parsing,bdHolderThe instance already contains the various properties we just set up in the configuration file, for exampleclass,id,name,aliasSuch attributes.
  • Decorate instance bdHolder: In this step, you are actually scanning the custom tags under the default tags, parsing the elements of these custom tags, and setting the custom attributes.
  • registeredbdHolderinformation: Parsing is complete, need to go to the containerbeanDefinitionMapRegistry registrationbeanInformation, registration operations delegated toBeanDefinitionReaderUtils.registerBeanDefinitionTo complete information registration through the tool class.
  • Send notification event: notifies the relevant listener, indicating thisbeanLoad completed

The interface describes what the method is supposed to do, and then breaks it down into smaller methods, where logic is processed, and methods can be reused.

So look at the source in addition to understand the framework of the implementation of logic, better to use and positioning problems, but also to learn the bosses write code when the design pattern, into their own work or learning ~


Parsing of other attributes of Bean tags

In the previous note, we have summarized the parsing of attributes ID and name, so we will not go into details. Here are the parsing of other attributes ~

First post the source code:

org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionElement(org.w3c.dom.Element, java.lang.String, org.springframework.beans.factory.config.BeanDefinition)

public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, @Nullable BeanDefinition containingBean) {
	this.parseState.push(new BeanEntry(beanName));
	String className = null;
	// Comment 2.3 Parsing class attributes
	if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
		className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
	}
	String parent = null;
	// Parse the parent attribute
	if(ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); }.../ / create GenericBeanDefinition
	AbstractBeanDefinition bd = createBeanDefinition(className, parent);
	// Parse the various properties of the default bean
	parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
	// Extract description desc
	bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
	// Parse meta attributes
	parseMetaElements(ele, bd);
	// resolve the lookup-method property
	parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
	// Parse the replace-method attribute
	parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
	// Parse the constructor
	parseConstructorArgElements(ele, bd);
	// Parse the property child element
	parsePropertyElements(ele, bd);
	Parse the qualifier child element
	parseQualifierElements(ele, bd);

	bd.setResource(this.readerContext.getResource());
	bd.setSource(extractSource(ele));

	returnbd; . }Copy the code

This is a complete property resolution process, including meta, lookup-method, replace-mthod and other property resolution.

Although not commonly used, but we learn a more attribute, when the encounter suitable for the use of the scene can be used, there is no need to panic encountered these attributes, I will first talk about what is used, and how to use, so that you have an impression ~


Create GenericBeanDefinition

The inheritance system of GenericBeanDefinition was covered in the last article, so here’s a quick explanation of how this method works:

createBeanDefinition(className, parent);

As you can see from the name of the method, its purpose is to create a beanDefinition that holds instances of properties.

When GenericBeanDefinition is instantiated in the last step, it also determines whether or not the class loader exists. If present, use the JVM of the classloader to load the class object, otherwise simply record the className.


Parse the various properties of the default bean

parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);

There are a lot of code implementations for this method, so if you are interested, you can find this method globally in the code base I uploaded, which has an introduction to each method

Briefly described, this method is used to parse each of the underlying attributes in the

tag, as follows:

  • scope
  • abstract
  • lazy-init
  • autowire
  • depends-on
  • autowire-candidate
  • primary
  • init-method
  • destroy-method
  • factory-method
  • factory-bean

As you can clearly see, Spring does the parsing of all bean properties, some of which are often used, such as autowire auto-weaving and init-method defining which method to call initially. And some words, students need to learn their own in-depth understanding ~


Parsing meta Attributes

Let’s talk about the use of the meta attribute.

<bean id="book" class="domain.SimpleBook">
	<! -- Meta tag -->
	<meta key="test_key" value="test_value"/>
</bean>
Copy the code

This meta property is not reflected in the object’s properties, but is an additional declaration in parseMetaElements(ele, bd); Methods to get in, the specific implementation is the element object getAttribute (key), to set the yuan in BeanMetadataAttributeAccessor object attributes

Because the code is relatively simple, so through the picture to illustrate:

The final attribute value is stored as key-value in the linked list Map

Attributes. The value can be obtained by using only the key value. In future code design, for the sake of expansibility, key-value can also be stored and used.
,>


Resolve the lookup-method property

This property is also not commonly used, as described in the book

It is usually injected as a getter. Getter injection is a special method injection that declares a method to return a bean of a certain type, but the bean to return is actually configured in the configuration file. The second method can be used to design some pluggable functionality, relieving program dependencies.

It’s a bit too much code, so LET me post a picture to introduce the key information:

First, I define a base object BaseBook and two inherited objects SimpleBook and ComplexBook. I also create a new abstract class and set a method getDomain. The return type is the base object.

The

property is used to load AbstractGetBookTest objects. The

property is used to load AbstractGetBookTest objects.

config.xml

<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd">


	<bean id="getBookTest" class="base.label.parsing.AbstractGetBookTest">
		<! 2.6 loop-up properties ๐ŸŒฐ -->
		<! The name of the method is injected into the getter, and the bean is injected into the class.
		<lookup-method name="getDomain" bean="complexBook"/>
	</bean>

	<bean id="book" class="domain.SimpleBook">
		<! -- Meta tag -->
		<meta key="test_key" value="test_value"/>
	</bean>

	<bean id="complexBook" class="domain.ComplexBook"/>

</beans>
Copy the code

Spring dynamically proxies the class specified by the bean, identifies the method specified by the name property, and returns the bean instance object specified by the bean property.

Since it is called getter injection, we can replace bean=”complexBook” with bean=”simpleBook”, so that the injected class becomes simpleBook object, and only need to modify the configuration file to change the class injection ~

The

code is similar to the meta properties, so it is easy to read


Resolve the appet-method attribute

What this method is for: You can replace existing methods at run time with new methods. Not only can you dynamically replace the returned entity bean, but you can also dynamically change the logic of the original method.

In simple terms, A method defined by A class is replaced at run time by another method, for example, A method is called in code, but B method is actually run.

See from the picture, the output frame to print out copy, after my replacement implement not difficult also, replacing the need to implement org. Springframework. Beans. Factory. Support. MethodReplacer interface, and then rewrite reimplement method, The key is in the < appet-method > property of the configuration file:

<bean id="beforeMethodReplaced" class="base.label.parsing.BeforeMethodReplaced">
	<! -- Comment 2.7 Method substitution -->
	<replaced-method name="printDefaultName" replacer="testMethodReplaced"/>
</bean>
Copy the code

Again, Spring recognizes the method specified by the name attribute in the appet-Method element and replaces it with the reImplement method of the specified bean instance object.

In the process of code parsing, the identified attribute is saved in the Set

overrides of MethodOverrides, which will finally be recorded in MethodOverrides of AbstractBeanDefinition.

Personally don’t recommend this method of use, if the regular work, in the circumstances of a comparatively strong business drivers, if like this, this method will lead to misunderstanding others intentions, if you want to call the query method, has been a dynamic proxy, call the delete method, it will lead to unnecessary BUG (ok I didn’t meet ha ha ha).


Parse the constructor-arg attribute

The parser constructor property is very common, but it can also be complicated to parse. Here is an example configuration:

<bean id="testConstructorArg" class="base.label.parsing.TestConstructorArg">
	<! If there are more than two constructors, the parsing will be more complicated.
	<constructor-arg index="0" value="JingQ"/>
	<constructor-arg index="1" value="23"/>
</bean>
Copy the code

What this configuration does is simple: automatically find the corresponding constructor for TestConstructorArg, and then inject value into the corresponding property based on the index index to implement the constructor.

Specific resolution in this method:

Parse constructor-arg sub-elements of the given bean element. */
public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
	NodeList nl = beanEle.getChildNodes();
	for (int i = 0; i < nl.getLength(); i++) {
		Node node = nl.item(i);
		if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
		    // loop through the constructor-arg attributeparseConstructorArgElement((Element) node, bd); }}}Copy the code

There are too many codes and I will not post them. If you are interested, please locate to the place where I wrote the comments and have a look in detail

Here’s a look at the flow of parsing constructor code:

โ‘  The index attribute is specified in the configuration

  • parsingconstructor-argThe child elements
  • useConstructorArgumentValues.ValueHolder(value)Type to encapsulate parsed elements (containingtype name indexAttributes)
  • addIndexedArgumentValueMethod will be parsed aftervalueAdd to currentBeanDefinition ็š„ ConstructorArgumentValues ็š„ indexedArgumentValuesProperties of the

โ‘  The index attribute is not specified in the configuration

  • parsingconstructor-argThe child elements
  • useConstructorArgumentValues.ValueHolder(value)Type to encapsulate parsed elements (containingtype name indexAttributes)
  • addGenericArgumentValueMethod will be parsed aftervalueAdd to currentBeanDefinition ็š„ ConstructorArgumentValues ็š„ genericArgumentValuesProperties of the

The difference between these two processes is that the final parsed attribute information is stored in different locations. With a specified subscript, it is saved to the indexedArgumentValues attribute, and without a specified subscript, it is saved to genericArgumentValues.

As can be seen, the first step and the second part of the two pieces of code are actually the same logic, there is the situation of repeated code, when I first study and work, in order to speed up, there are a lot of such repeated type of code.

After slowly learn more knowledge and design patterns, look back before writing code, had a delete rewrite the impulse, so if if written in the first place, take the same processing logic of the code, and then for code reuse, reduce code duplication rate, make the code look better, so you won’t have to be and his joke about ฮฃ (o ใ‚š ะด ใ‚š o ใƒŽ)

The ref value attribute is relatively easy to handle, so you can see how it is resolved by looking at the code. The child element is more difficult to handle, as in the following example:

<constructor-arg>
    <map>
        <entry key="key" value="value" />
    </map>
</constructor-arg>
Copy the code

The specific method of parsing child elements is: org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parsePropertySubElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition, java.lang.String)

This method is mainly used to parse various sub-elements, including idref value array set map and other sub-elements of the machine, here is not detailed, students interested in continuing to track it ~


Parsing property properties

Usage in accessory files:

<! -- Property >
<bean id="testPropertyParseElement" class="base.label.parsing.TestPropertyParseElement">
	<property name="id" value="1"/>
	<property name="name" value="JingQ"/>
</bean>
Copy the code

This parse entry method is similar to the constructor-arg parse entry method, with the following code:

Parse property sub-elements of the given bean element. */
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
	NodeList nl = beanEle.getChildNodes();
	for (int i = 0; i < nl.getLength(); i++) {
		Node node = nl.item(i);
		if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
			// Loop through property propertiesparsePropertyElement((Element) node, bd); }}}Copy the code

This entry method extracts all the children of the property, then calls the parsePropertyElement method to process it, and then wraps it with PropertyValue. The last is recorded in the propertyValues property in the BeanDefinition.

Experienced the above complex attribute parsing, property attribute parsing appears relatively simple, are the same routine, loop through the element parsing, so familiar with the preceding parsing logic, see the code behind it will be faster to understand ~


Parse the Qualifer property

You’re probably more familiar with the @qualifer tag, which serves the same purpose as the Qualifer attribute.

When using the Spring framework for class injection, there must be one and only one matching candidate bean. If no matching bean is found, the container throws a BeanCreationException.

For example, if we define an abstract class AbstractBook, we have two concrete implementation classes Book1 and Book2.

@Autowired
private AbstractBook book;
Copy the code

The runtime will throw the error exception we just mentioned, and we can disambiguate it in two ways:

โ‘  Set quailfer in the configuration file

Specify the name of the injection bean through the Qualifier

<bean id="testBean" class="base.TestBean">
    <qualifer type="org.Springframeword.beans.factory.annotation.Quailfier" value="book1"/>
</bean>
Copy the code

(2) using the @ the Qualifier (” beanNeame “)

@Qualifier("book1")
private AbstractBook book;
Copy the code

Similarly, the code parsing process is similar to the previous routine, leave it to the students to analyze ~


conclusion

Let’s review the general resolution flow:

  • Determining the element typeIn each entry method, there is a judgment methodnodeNameEquals(node, XXXX_METHOD_ELEMENT)Only those that match the type are parsed
  • parsing: When parsing a child element, most of the time you see yeskey-valueAttribute pairs of forms, throughele.getAttribute(NAME_ATTRIBUTE)And so on
  • storage: Stores the result of the previous parsingbeanDefinitionCorresponding attributes

Such a look, is not a clear sense of the source code analysis is not so afraid.

In the next article, I will introduce the process of parsing the default tags. See you next


The resources

  1. — Beijing: Posts and Telecommunications Press

Portal:

  • Spring source learning – environment preparation

  • (1) The infrastructure of the container

  • Spring source code learning (2) default tag parsing

  • Spring source code learning (3) custom tags

  • Spring source code learning (four) bean loading

  • Spring source code learning (5) loop dependency

  • Spring source code learning (six) extension function part 1

  • Spring source code learning (seven) extension features part 2

  • Spring source learning (eight) AOP use and implementation principle

  • Spring source code learning (9) Transaction Transaction

  • (10) Spring MVC