9 spring-Custom Label Resolution

Original Link: https://my.oschina.net/u/1590001/blog/268194

At this point, we have finished the process of parsing and extracting the default labels for analysis. Maybe there is too much design work. We forget which function we started with.

Let's review again the starting method of the default tag resolution method.

 

The entrance is as follows:

 1 /**
 2      * Parse the elements at the root level in the document: "import", "alias", "bean".
 3      * 
 4      * @param root the DOM root element of the document
 5      */
 6     protected void parseBeanDefinitions(Element root,
 7             BeanDefinitionParserDelegate delegate) {
 8         // Yes Bean Processing
 9         if (delegate.isDefaultNamespace(root)) {
10             NodeList nl = root.getChildNodes();
11             for (int i = 0; i < nl.getLength(); i++) {
12                 Node node = nl.item(i);
13                 if (node instanceof Element) {
14                     Element ele = (Element) node;
15                     if (delegate.isDefaultNamespace(ele)) {
16                         // Yes Bean Processing,If Default
17                         parseDefaultElement(ele, delegate);
18                     }
19                     else {
20                         // Yes Bean Processing,If Custom
21                         delegate.parseCustomElement(ele);
22                     }
23                 }
24             }
25         }
26         else {
27             delegate.parseCustomElement(root);
28         }
29     }

The parsing method is as follows:

 1     private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
 2         // Yes<import>node, call importBeanDefinitionResource Method parsing, In this method, Go back to the first step to read the configuration file and parse it. Such a recursive loop.
 3         if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
 4             importBeanDefinitionResource(ele);
 5         }
 6         // Yes<alias>node, call processAliasRegistration Alias resolution
 7         else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
 8             processAliasRegistration(ele);
 9         }
10         // <bean>Node Call processBeanDefinition Resolve
11         else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
12             processBeanDefinition(ele, delegate);
13         }
14         // <beans>Processing
15         else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
16             // recurse
17             doRegisterBeanDefinitions(ele);
18         }
19     }

Core approach:

 1 /**
 2      * Process the given bean element, parsing the bean definition and registering it with
 3      * the registry.
 4      */
 5     protected void processBeanDefinition(Element ele,
 6             BeanDefinitionParserDelegate delegate) {
 7         // Entrust BeanDefinition Class parseBeanDefinitionElement Method for element parsing,Return Beandefinition
 8         // Instances of types bdHolder After this method,
 9         // bdHolder Instances already contain properties from our profile,for example : class,name,id,alias
10         BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
11         if (bdHolder != null) {
12             //When returned bdHolder Not empty,If there are any more custom attributes under child nodes with default labels,Custom tags need to be resolved again.
13             bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
14             try {
15                 // Register the final decorated instance.
16                 // After parsing is complete,Need to parse bdHolder Register,Similarly, registration was delegated to BeanDefinitionUtils Of registerBeanDefinition
17                 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,
18                         getReaderContext().getRegistry());
19             }
20             catch (BeanDefinitionStoreException ex) {
21                 getReaderContext().error(
22                         "Failed to register bean definition with name '"
23                                 + bdHolder.getBeanName() + "'", ele, ex);
24             }
25             // Send registration event.
26             // Last Respond Event,Notify related listeners,this bean Already loaded.
27             getReaderContext().fireComponentRegistered(
28                     new BeanComponentDefinition(bdHolder));
29         }
30     }

Earlier, we had spent a lot of time analyzing BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); this code

Next is the focus of this chapter:

  bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
First of all, let's look at the role of this statement. Semantically, this code is all about decorating the BeanDefinition if needed; what does this mean?
The code actually works for a scenario like this:
<bean id="animal" class="test.constructor.Animal">
        <myTag:my   value="p1"/> 
    </bean>

Well, let's not talk much, let's continue with the logic of this code;

 

 1 public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele,
 2             BeanDefinitionHolder definitionHolder) {
 3         /*
 4          * The third function is set to null here, so what does the third function do and under what circumstances is it not empty?
 5          * 
 6          * In fact, the third parameter is the parent beans, where you pass the parent beanDefinition when analyzing a nested configuration
 7          * .Analyzing the source code shows that the parameters passed here are actually meant to use the scope property of the parent class, in case the child class defaults to the parent class's property if the scope is not set. This analysis is for the top level configuration, so pass null
 8          * ,
 9          */
10         return decorateBeanDefinitionIfRequired(ele, definitionHolder, null);
11     }

 

The third function is set to null, so what does the third function do and under what circumstances is it not empty?

Actually, the third parameter is the parent bean. When analyzing a nested configuration, you need to pass the parent beanDefinition. To analyze the source code, you know that the parameter passed here is actually to use the scope property of the parent class, in case the parent class defaults to the property if the scope is not set. Here the top-level configuration is analyzed.So pass Null and refer to the third Null as Null for further tracking;

The code is as follows:

 

 1 public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele,
 2             BeanDefinitionHolder definitionHolder, BeanDefinition containingBd) {
 3 
 4         BeanDefinitionHolder finalDefinition = definitionHolder;
 5 
 6         // Decorate based on custom attributes first.
 7         // Traverse through all attributes,See if there are any attributes for decoration
 8         NamedNodeMap attributes = ele.getAttributes();
 9         for (int i = 0; i < attributes.getLength(); i++) {
10             Node node = attributes.item(i);
11             finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
12         }
13 
14         // Decorate based on custom nested elements.
15         // Traverse through all attributes,See if there are any child elements for decoration
16         NodeList children = ele.getChildNodes();
17         for (int i = 0; i < children.getLength(); i++) {
18             Node node = children.item(i);
19             if (node.getNodeType() == Node.ELEMENT_NODE) {
20                 finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
21             }
22         }
23         return finalDefinition;
24     }

From the code above, we can see that the decorateIfRequired() method is called on all attributes and nodes of an element, and we continue to trace the code

 1     public BeanDefinitionHolder decorateIfRequired(Node node,
 2             BeanDefinitionHolder originalDef, BeanDefinition containingBd) {
 3         // Get Namespace
 4         String namespaceUri = getNamespaceURI(node);
 5         // Modify non-default labels
 6         if (!isDefaultNamespace(namespaceUri)) {
 7             // Find the corresponding processor based on the namespace
 8             NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(
 9                     namespaceUri);
10             if (handler != null) {
11                 // Modify
12                 return handler.decorate(node, originalDef, new ParserContext(
13                         this.readerContext, this, containingBd));
14             }
15             else if (namespaceUri != null
16                     && namespaceUri.startsWith("http://www.springframework.org/")) {
17                 error("Unable to locate Spring NamespaceHandler for XML schema namespace ["
18                         + namespaceUri + "]", node);
19             }
20             else {
21                 // A custom namespace, not to be handled by Spring - maybe "xml:...".
22                 if (logger.isDebugEnabled()) {
23                     logger.debug("No Spring NamespaceHandler found for XML schema namespace ["
24                             + namespaceUri + "]");
25                 }
26             }
27         }
28         return originalDef;
29     }

  

The program has come to this point quite clearly. First, it obtains the attribute or namespace of the element to determine if the element or attribute is suitable for the custom label's parsing conditions and to find out what the custom type corresponds to.

NamespaceHandler and further parse,
To summarize the role of decorateIfRequired, in decorateIfRequired, we can see that the default attributes or elements of a program are directly ignored in the past because they have been handled before.
Only custom tags are of interest here. This method implements finding custom tags, finding namespace processors based on custom tags, and further parsing.


Reprinted at: https://my.oschina.net/u/1590001/blog/268194

Keywords: Spring xml Attribute

Added by JD-AM on Sun, 15 Sep 2019 05:47:17 +0300