到这里,我们已经完成了分析默认标签的解析与提取过程,或许设计的内容太多,我们忘了我们是冲哪个函数开始了的,
让我们再次回顾一下默认标签解析方法的起始方法.
入口如下:
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 // 对Bean的处理 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 // 对Bean的处理,如果是默认 17 parseDefaultElement(ele, delegate); 18 } 19 else { 20 // 对Bean的处理,如果是自定义 21 delegate.parseCustomElement(ele); 22 } 23 } 24 } 25 } 26 else { 27 delegate.parseCustomElement(root); 28 } 29 }
解析方法如下:
1 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { 2 // 对<import>节点, 调用importBeanDefinitionResource方法解析, 此方法中, 又回到第一步读取配置文件并解析. 如此递归循环. 3 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { 4 importBeanDefinitionResource(ele); 5 } 6 // 对<alias>节点, 调用processAliasRegistration进行别名解析 7 else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { 8 processAliasRegistration(ele); 9 } 10 // <bean>节点调用processBeanDefinition进行解析 11 else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { 12 processBeanDefinition(ele, delegate); 13 } 14 // <beans>的处理 15 else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { 16 // recurse 17 doRegisterBeanDefinitions(ele); 18 } 19 }
核心方法:
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 // 委托BeanDefinition类的parseBeanDefinitionElement方法进行元素解析,返回Beandefinition 8 // 类型的实例bdHolder 经过这个方法之后, 9 // bdHolder实例已经包含了我们配置文件中的各种属性了,例如 : class,name,id,alias 10 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); 11 if (bdHolder != null) { 12 //当返回的bdHolder 不为空的情况下,若存在默认标签的子节点下再有自定义属性,还需要再次对自定义标签进行解析. 13 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); 14 try { 15 // Register the final decorated instance. 16 // 解析完成之后,需要对解析后的bdHolder 进行注册,同样注册操作委托给了BeanDefinitionUtils 的 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 // 最后发出响应事件,通知相关的监听器,这个bean已经加载完了. 27 getReaderContext().fireComponentRegistered( 28 new BeanComponentDefinition(bdHolder)); 29 } 30 }
之前,我们已经用了大量的时间分析了 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); 这句代码;;
接下来就是我们本章的重点:
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
首先我们先了解这局话的作用,从语义上讲,这段的码的意识无非是: 如果有需要的话,就对BeanDefinition 进行装饰;那么这句话到底是什么意思呢?
其实代码适用于这样的一个场景:
<bean id="animal" class="test.constructor.Animal"> <myTag:my value="p1"/> </bean>
好了,废话不多讲,我们继续分析下这段代码的逻辑;
1 public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, 2 BeanDefinitionHolder definitionHolder) { 3 /* 4 * 这里将第三个函数设置为null, 那么第三个函数是做什么用的呢? 什么情况下不为空呢? 5 * 6 * 其实这第三个参数是父类bean,当对某个嵌套配置进行分析时,这里需要传递父类beanDefinition 7 * .分析源代码得知,这里传递的参数其实是为了使用父类的scope属性,以备子类若没有设置scope 时默认使用父类的属性,这里分析的是顶层配置,所以传null 8 * , 9 */ 10 return decorateBeanDefinitionIfRequired(ele, definitionHolder, null); 11 }
这里将第三个函数设置为null, 那么第三个函数是做什么用的呢? 什么情况下不为空呢?
其实这第三个参数是父类bean,当对某个嵌套配置进行分析时,这里需要传递父类beanDefinition .分析源代码得知,这里传递的参数其实是为了使用父类的scope属性,以备子类若没有设置scope 时默认使用父类的属性,这里分析的是顶层配置,所以传null , 将第三个null 是指为 Null 后进一步追踪;
代码如下:
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 // 遍历所有属性,看看是否有适用于修饰的属性 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 // 遍历所有属性,看看是否有适用于修饰的子元素 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 }
上面的代码,我们可以看到该方法分别对元素的所有属性和节点进行了decorateIfRequired()方法的调用,我们继续跟踪代码
1 public BeanDefinitionHolder decorateIfRequired(Node node, 2 BeanDefinitionHolder originalDef, BeanDefinition containingBd) { 3 // 获取命名空间 4 String namespaceUri = getNamespaceURI(node); 5 // 对非默认标签进行修饰 6 if (!isDefaultNamespace(namespaceUri)) { 7 // 根据命名空间找到对应的处理器 8 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve( 9 namespaceUri); 10 if (handler != null) { 11 // 进行修饰 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 }
程序走到这里已经相当清楚了,首先获取属性或者元素的命名空间,以此来判断该元素或属性是否适用于自定义标签的解析条件,找出自定义类型对应的
NamespaceHandler 并进一步解析,
我们总结一下 decorateIfRequired 的作用,在 decorateIfRequired 中,我们可以看到对于程序默认的属性或元素是直接忽略过去的,因为在之前已经处理过了,
这里只对自定义标签感兴趣,在这个方法中实现了寻找自定义标签,并根据自定义标签寻找命名空间处理器,并进一步的解析;