【spring源码-3】xml解析(下)自定义标签解析

326 阅读4分钟

上一篇【spring源码-2】xml解析(中)默认标签解析

引题

上篇回顾:
< bean >标签解析就是将一个一个的< bean >标签转换为 BD(BeanDefinition) 对象,转换时将标签里面的配置属性等元素设置到 BD 对象中,然后再将 BD 存入 BD工厂(DefaultListableBeanFactory)的缓存Map(beanDefinitionMap)中。

本文将描述自定义标签的解析流程,以 < component-scan > 标签为例。

自定义标签解析主流程

< component-scan >

image.png

DefaultBeanDefinitionDocumentReader 类

image.png 176 行:默认标签解析,见上篇。
179 行:自定义标签解析。

BeanDefinitionParserDelegate 类

image.png 1382 行:获取到 < component-scan > 标签的 namespaceURI


1386 行:获取 NamespaceHandlerResolver 并调用 resolve 方法。

DefaultNamespaceHandlerResolver 类

image.png 类描述:
NamespaceHandlerResolver接口的默认实现。 根据映射文件中包含的映射,将命名空间 URI 解析为实现类。 默认情况下,此实现在META-INF/spring.handlers查找映射文件,但这可以使用DefaultNamespaceHandlerResolver(ClassLoader, String)构造函数进行更改。

image.png image.png 118 行:获取spring所有jar包中的 "META-INF/spring.handlers" 文件,并且建立 namespaceURI 与 NamespaceHandler 类的映射关系。点击进入 153 行方法。如下图:
image.png 163 - 164 行:加载 "META-INF/spring.handlers" 文件的过程,入参 this.handlerMappingsLocation 就是 "META-INF/spring.handlers",如下图:
image.png 168 -170 行:将建设好的映射关系设置给当前类。
119 行:根据 namespaceURI 找到 对应的 NamespaceHandler 处理类。
134 行:创建该处理类。
135 行:调用该处理类的 init方法。 < component-scan > 标签对应的处理类是 org.springframework.context.config.ContextNamespaceHandler。


ContextNamespaceHandler 类

image.png init方法:注册各个标签对应的解析类。由 NamespaceHandlerSupport 类完成注册。

NamespaceHandlerSupport 类

image.png image.png 138 行:建立标签名和解析类的映射关系。

135 行结束。

136 行:再次建立 namespaceURI 与 NamespaceHandler 类的映射关系并添加到本类 70 行的map中。 137 行:返回 NamespaceHandler。


为方便阅读,再次贴图:

image.png 1386 行结束。这一行走完,就拿到了标签对应的解析类。
1391 行:调用解析类的解析方法。 还是由 NamespaceHandlerSupport 类完成解析

NamespaceHandlerSupport 类

image.png 73 行:前面已经完成了标签与对应的解析类的映射,保存在该类 51 行的map中。
74 行:从map中拿到解析类(BeanDefinitionParser)并调用 parse 方法。

ComponentScanBeanDefinitionParser 类

image.png image.png 83 行:获取到< component-scan >标签的 base-package 属性,就是配置的包路径 "com.springdemo.bean"。
85 行:base-package 属性值可以是多个包路径,逗号隔开,所以解析成一个数组。 89 行:调用96行方法。
98 行:判断 < component-scan > 标签有没有配置 use-default-filters 属性,默认为 true,意思是是否使用默认的过滤规则。
103 行:创建注解扫描器。调用到131行,然后调用到 ClassPathBeanDefinitionScanner 类的构造函数。

ClassPathBeanDefinitionScanner 类

image.png 166 行:注册默认的过滤注解。调到 ClassPathScanningCandidateComponentProvider 类的方法。
206 行:可以直观看出默认的过滤注解就是 @Component。

103 行创建注解扫描器完。

回过头再看注解扫描器扫描注解的逻辑:
为方便阅读,再次贴图: image.png 89 行:创建好了注解扫描器。
90 行:开始扫描注解。

image.png 276 行:循环包路径,并获取到包路径下有 @Component 注解的类。
277 行:循环并填充注解信息。
285 行:填充注解信息(@Lazy @Role @DependOn @Description @Primary)
292 行:注册BeanDefinition。与默认标签一样,还是用的 BeanDefinitionReaderUtils 类完成注册。

90 行结束,此时已经将含有 @Component 注解的类转换成了BeanDefinition 并完成了注册。

91 行:注册PostProcessor,重点。 首先调用本类方法。如下图:

image.png

然后由 AnnotationConfigUtils 完成注册。如下图:

image.png image.png image.png image.png 166 行:org.springframework.context.annotation.internalConfigurationAnnotationProcessor BD注册的Map没有该类,就注册进去。
172 行:org.springframework.context.annotation.internalAutowiredAnnotationProcessor BD注册的Map没有该类,就注册进去。
179 行:org.springframework.context.annotation.internalCommonAnnotationProcessor BD注册的Map没有该类,就注册进去。

91 行注册PostProcessor结束。


为方便阅读,再次贴图:

image.png 1391 行:到此,< component-scan > 解析完成。


回到最初的地方:

image.png 179 行:本地循环的标签解析完成,开始下一位。

总结

自定义标签解析流程:

  1. 首先根据标签的 namespaceURI 找到对应的 BeanDefinitionHandler。找的过程就是先把所有jar包里面的 "META-INF/spring.handlers" 文件进行解析并建立 namespaceURI 与 BeanDefinitionHandler 的映射关系,再从映射关系的Map里面根据 namespaceURI 获取到对应的 BeanDefinitionHandler。
  2. 然后调用 BeanDefinitionHandler 的 init 方法,该方法会建立 标签名称 和 对应标签解析类的映射关系,也是一个Map。
  3. 最后找到了标签的解析类,就调用该类的 parse 方法进行具体的标签解析逻辑。
  4. 标签解析完成之后还会有一个操作:注册 PostProcessor,spring 会将一些 PostProcessor 类在完成自定义标签解析之后完成 BD 的注册。

注:本文通过源码 + 行说明的方式进行描述,若不好理解可留言。本文仅为个人学习记录,有错误的地方,请大佬们指正。

下一篇【spring源码-4】bean的实例化(1)准备工作