到这里,我们已经完成了分析默认标签的解析与提取过程,或许设计的内容太多,我们忘了我们是冲哪个函数开始了的,
让我们再次回顾一下默认标签解析方法的起始方法.
入口如下:
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 中,我们可以看到对于程序默认的属性或元素是直接忽略过去的,因为在之前已经处理过了,
这里只对自定义标签感兴趣,在这个方法中实现了寻找自定义标签,并根据自定义标签寻找命名空间处理器,并进一步的解析;