Spring Source analysis Chapter 8, continue.
In the last article, we analyzed the parsing process of bean tags, but mainly involved some simple properties. Some unpopular properties, such as look-method, were not analyzed with us, mainly considering that people might use these properties less, so I recorded a simple video last week. Let’s review the usage of these unpopular attributes:
Four cool attributes in Spring that you may not have used, challenge to see!
Now that you know about bean node configuration, let’s look at the full parsing process.
Read the previous articles in this series to better understand this article:
- Spring source code interpretation plan
- Spring source code first open whole! How is the configuration file loaded?
- Spring source code second bomb! XML file parsing process
- Spring source code third bullet! What the hell is EntityResolver?
- Spring source code fourth bomb! Understand the BeanDefinition in depth
- Build Spring source code analysis environment for you
- Spring source code sixth bullet! The father of scene and people talk about the container DefaultListableBeanFactory
- Spring source code interpretation of the seventh bullet! Bean label parsing
1. Review of analytical methods
In the last article, we finally analyzed the following method:
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
parseMetaElements(ele, bd);
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
parseConstructorArgElements(ele, bd);
parsePropertyElements(ele, bd);
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
this.parseState.pop();
}
return null;
}
Copy the code
ParseBeanDefinitionAttributes method is used to resolve the common properties, we have been analyzed in the previous article, go here, mainly to see several other methods of resolving the work today.
2.Description
. The first is the description of parsing, directly through DomUtils getChildElementValueByTagName tool method from the node to retrieve the description attribute’s value. There’s nothing to say about that.
If you are not sure about the function of the tool or want to verify its other uses, you can use the Evaluate Expression function provided by IDEA to test your IDEA. Enter the code you want to execute:
3.parseMetaElements
This method mainly resolves meta attributes. As we’ve seen in the previous video, this meta property is stored in and retrieved from a BeanDefinition, so it’s easy to see what the parsing code looks like:
public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
NodeList nl = ele.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
Element metaElement = (Element) node;
String key = metaElement.getAttribute(KEY_ATTRIBUTE);
String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
BeanMetadataAttribute attribute = newBeanMetadataAttribute(key, value); attribute.setSource(extractSource(metaElement)); attributeAccessor.addMetadataAttribute(attribute); }}}Copy the code
As you can see, you iterate over the element, extract the meta element’s value, and build the BeanMetadataAttribute object, which is then stored in the GenericBeanDefinition object.
A friend said in the BeanMetadataAttributeAccessor, isn’t it? This is actually GenericBeanDefinition parent, BeanMetadataAttributeAccessor is dedicated to the processing properties of load and read, related introduction can refer to scene in front of the article:
- Spring source code fourth bomb! Understand the BeanDefinition in depth
4.parseLookupOverrideSubElements
This method is to understand the separation of the properties of the lookup-method, in the previous video Songko has talked with you, lookup-method can dynamically replace the running method, according to this idea, we look at the source of this method:
public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
Element ele = (Element) node;
String methodName = ele.getAttribute(NAME_ATTRIBUTE);
String beanRef = ele.getAttribute(BEAN_ELEMENT);
LookupOverride override = newLookupOverride(methodName, beanRef); override.setSource(extractSource(ele)); overrides.addOverride(override); }}}Copy the code
As you can see, I’m iterating through the element, and from the look-up method property, I’m getting the methodName and the beanRef property, Construct LookupOverride and store it in the methodOverrides property of GenericBeanDefinition.
After storing it in the methodOverrides attribute of GenericBeanDefinition, we can also view it in the code:
5.parseReplacedMethodSubElements
ParseReplacedMethodSubElements this method mainly analysis the replace – attributes of the method, according to the video, the interpretation of the replace – method to realize dynamic replacement method, and can be modified when replacing method.
In this vein, the method is easy to understand:
public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) {
Element replacedMethodEle = (Element) node;
String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE);
String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE);
ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);
// Look for arg-type match elements.
List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);
for (Element argTypeEle : argTypeEles) {
String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE);
match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));
if(StringUtils.hasText(match)) { replaceOverride.addTypeIdentifier(match); } } replaceOverride.setSource(extractSource(replacedMethodEle)); overrides.addOverride(replaceOverride); }}}Copy the code
Name gets the old method to be replaced, callback gets the new method to be replaced, and the ReplaceOverride object is constructed.
In addition, since replace-method can also be configured with parameter types, the arG-type needs to be resolved after the ReplaceOverride object is constructed.
6.parseConstructorArgElements
ParseConstructorArgElements this method is mainly used to resolve construction method. This is something you should deal with a lot in your daily development. If you are not familiar with the various construction method injection, you can reply to Spring5 in the wechat public account Jiangnan Little Rain background, and get the free Spring introductory tutorial recorded by Songko, which is described in it.
Let’s look at the parsing of the constructor:
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)) { parseConstructorArgElement((Element) node, bd); }}}public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
if (StringUtils.hasLength(indexAttr)) {
try {
int index = Integer.parseInt(indexAttr);
if (index < 0) {
error("'index' cannot be lower than 0", ele);
}
else {
try {
this.parseState.push(new ConstructorArgumentEntry(index));
Object value = parsePropertyValue(ele, bd, null);
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {
error("Ambiguous constructor-arg entries for index " + index, ele);
}
else{ bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder); }}finally {
this.parseState.pop(); }}}catch (NumberFormatException ex) {
error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele); }}else {
try {
this.parseState.push(new ConstructorArgumentEntry());
Object value = parsePropertyValue(ele, bd, null);
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
}
finally {
this.parseState.pop(); }}}Copy the code
As you can see, the constructor eventually parseConstructorArgElement analytic method.
- Get the name, index, and value attributes first, because the constructor argument can specify either name or subscript.
- Determine whether index has a value, and then determine whether to parse by index or by name.
- Either way, the value is resolved using the parsePropertyValue method.
- Parsing the child elements stored in the ConstructorArgumentValues. ValueHolder object.
- If arguments are parsed through index, the addIndexedArgumentValue method is eventually called to save the parsed results, and if arguments are parsed through name, the addGenericArgumentValue method is eventually called to save the parsed results.
7.parsePropertyElements
The parsePropertyElements method is used to resolve property injection.
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)) { parsePropertyElement((Element) node, bd); }}}public void parsePropertyElement(Element ele, BeanDefinition bd) {
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
if(! StringUtils.hasLength(propertyName)) { error("Tag 'property' must have a 'name' attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
Object val = parsePropertyValue(ele, bd, propertyName);
PropertyValue pv = new PropertyValue(propertyName, val);
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
this.parseState.pop(); }}Copy the code
After all we’ve seen, it’s a little easier to look at this method. Here the value is finally resolved through the parsePropertyValue method and the addPropertyValue method is called to store the relevant value.
8.parseQualifierElements
ParseQualifierElements are used to resolve the Qualifier node and are ultimately stored in the corresponding attribute. The parsing process is similar to the previous one, so I won’t repeat it. Let’s take a look at the parsing result:
9. Talk about BeanDefinition
The parsed properties are stored in the GenericBeanDefinition object, which can then be used to build a Bean.
In the Spring container, we commonly use Bean by Bean, and BeanDefinition is the definition of a Bean by name.
In fact, the Bean properties that we configure in the XML file are not only object related, but the Spring container has to deal with the Bean lifecycle, destruction, initialization, and so on. The operations we define about the Bean’s life cycle, destruction, initialization, and so on must always be carried by an object, and that object is the BeanDefinition.
The various attributes defined in XML are first loaded onto the BeanDefinition, and then a Bean is generated from the BeanDefinition. In this sense, the BeanDefinition and Bean relationship is somewhat similar to the class-object relationship.
In Spring, there are three main types of BeanDefinitions:
- RootBeanDefinition
- ChildBeanDefinition
- GenericBeanDefinition
In Spring, if we configure a parent Bean for a Bean, the parent Bean will be resolved to RootBeanDefinition and the child Bean to ChildBeanDefinition. If there is no parent Bean, Is resolved to RootBeanDefinition.
GenericBeanDefinition is a new BeanDefinition implementation class added since Spring2.5. GenericBeanDefinition can dynamically set the parent Bean and has both RootBeanDefinition and ChildBeanDefinition functions.
GenericBeanDefinition is commonly used today, so we can see that the previous parsing results are also saved into GenericBeanDefinition.
For more information on BeanDefinition, check out Songo’s previous post: Spring source code # 4! Understand the BeanDefinition in depth
Well, Spring chapter 8, we will talk about so much, if you feel harvest, remember to point under the encouragement of oh ~