[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
- show
demo
Code, 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 instance
bdHolder
: First delegateBeanDefinitionParserDelegate
Of the classparseBeanDefinitionElement
Method to parse elements. After parsing,bdHolder
The instance already contains the various properties we just set up in the configuration file, for exampleclass
,id
,name
,alias
Such 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.
- registered
bdHolder
information: Parsing is complete, need to go to the containerbeanDefinitionMap
Registry registrationbean
Information, registration operations delegated toBeanDefinitionReaderUtils.registerBeanDefinition
To complete information registration through the tool class. - Send notification event: notifies the relevant listener, indicating this
bean
Load 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
- parsing
constructor-arg
The child elements - use
ConstructorArgumentValues.ValueHolder(value)
Type to encapsulate parsed elements (containingtype
name
index
Attributes) addIndexedArgumentValue
Method will be parsed aftervalue
Add to currentBeanDefinition
็ConstructorArgumentValues
็indexedArgumentValues
Properties of the
โ The index attribute is not specified in the configuration
- parsing
constructor-arg
The child elements - use
ConstructorArgumentValues.ValueHolder(value)
Type to encapsulate parsed elements (containingtype
name
index
Attributes) addGenericArgumentValue
Method will be parsed aftervalue
Add to currentBeanDefinition
็ConstructorArgumentValues
็genericArgumentValues
Properties 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 method
nodeNameEquals(node, XXXX_METHOD_ELEMENT)
Only those that match the type are parsed - parsing: When parsing a child element, most of the time you see yes
key-value
Attribute pairs of forms, throughele.getAttribute(NAME_ATTRIBUTE)
And so on - storage: Stores the result of the previous parsing
beanDefinition
Corresponding 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
- — 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