引题
上篇回顾:
< bean >标签解析就是将一个一个的< bean >标签转换为 BD(BeanDefinition) 对象,转换时将标签里面的配置属性等元素设置到 BD 对象中,然后再将 BD 存入 BD工厂(DefaultListableBeanFactory)的缓存Map(beanDefinitionMap)中。
本文将描述自定义标签的解析流程,以 < component-scan > 标签为例。
自定义标签解析主流程
< component-scan >
DefaultBeanDefinitionDocumentReader 类
176 行:默认标签解析,见上篇。
179 行:自定义标签解析。
BeanDefinitionParserDelegate 类
1382 行:获取到 < component-scan > 标签的 namespaceURI
1386 行:获取 NamespaceHandlerResolver 并调用 resolve 方法。
DefaultNamespaceHandlerResolver 类
类描述:
NamespaceHandlerResolver接口的默认实现。 根据映射文件中包含的映射,将命名空间 URI 解析为实现类。
默认情况下,此实现在META-INF/spring.handlers查找映射文件,但这可以使用DefaultNamespaceHandlerResolver(ClassLoader, String)构造函数进行更改。
118 行:获取spring所有jar包中的 "META-INF/spring.handlers" 文件,并且建立 namespaceURI 与 NamespaceHandler 类的映射关系。点击进入 153 行方法。如下图:
163 - 164 行:加载 "META-INF/spring.handlers" 文件的过程,入参 this.handlerMappingsLocation 就是
"META-INF/spring.handlers",如下图:
168 -170 行:将建设好的映射关系设置给当前类。
119 行:根据 namespaceURI 找到 对应的 NamespaceHandler 处理类。
134 行:创建该处理类。
135 行:调用该处理类的 init方法。 < component-scan > 标签对应的处理类是 org.springframework.context.config.ContextNamespaceHandler。
ContextNamespaceHandler 类
init方法:注册各个标签对应的解析类。由 NamespaceHandlerSupport 类完成注册。
NamespaceHandlerSupport 类
138 行:建立标签名和解析类的映射关系。
135 行结束。
136 行:再次建立 namespaceURI 与 NamespaceHandler 类的映射关系并添加到本类 70 行的map中。 137 行:返回 NamespaceHandler。
为方便阅读,再次贴图:
1386 行结束。这一行走完,就拿到了标签对应的解析类。
1391 行:调用解析类的解析方法。 还是由 NamespaceHandlerSupport 类完成解析
NamespaceHandlerSupport 类
73 行:前面已经完成了标签与对应的解析类的映射,保存在该类 51 行的map中。
74 行:从map中拿到解析类(BeanDefinitionParser)并调用 parse 方法。
ComponentScanBeanDefinitionParser 类
83 行:获取到< component-scan >标签的 base-package 属性,就是配置的包路径 "com.springdemo.bean"。
85 行:base-package 属性值可以是多个包路径,逗号隔开,所以解析成一个数组。
89 行:调用96行方法。
98 行:判断 < component-scan > 标签有没有配置 use-default-filters 属性,默认为 true,意思是是否使用默认的过滤规则。
103 行:创建注解扫描器。调用到131行,然后调用到 ClassPathBeanDefinitionScanner 类的构造函数。
ClassPathBeanDefinitionScanner 类
166 行:注册默认的过滤注解。调到 ClassPathScanningCandidateComponentProvider 类的方法。
206 行:可以直观看出默认的过滤注解就是 @Component。
103 行创建注解扫描器完。
回过头再看注解扫描器扫描注解的逻辑:
为方便阅读,再次贴图:
89 行:创建好了注解扫描器。
90 行:开始扫描注解。
276 行:循环包路径,并获取到包路径下有 @Component 注解的类。
277 行:循环并填充注解信息。
285 行:填充注解信息(@Lazy @Role @DependOn @Description @Primary)
292 行:注册BeanDefinition。与默认标签一样,还是用的 BeanDefinitionReaderUtils 类完成注册。
90 行结束,此时已经将含有 @Component 注解的类转换成了BeanDefinition 并完成了注册。
91 行:注册PostProcessor,重点。 首先调用本类方法。如下图:
然后由 AnnotationConfigUtils 完成注册。如下图:
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结束。
为方便阅读,再次贴图:
1391 行:到此,< component-scan > 解析完成。
回到最初的地方:
179 行:本地循环的标签解析完成,开始下一位。
总结
自定义标签解析流程:
- 首先根据标签的 namespaceURI 找到对应的 BeanDefinitionHandler。找的过程就是先把所有jar包里面的 "META-INF/spring.handlers" 文件进行解析并建立 namespaceURI 与 BeanDefinitionHandler 的映射关系,再从映射关系的Map里面根据 namespaceURI 获取到对应的 BeanDefinitionHandler。
- 然后调用 BeanDefinitionHandler 的 init 方法,该方法会建立 标签名称 和 对应标签解析类的映射关系,也是一个Map。
- 最后找到了标签的解析类,就调用该类的 parse 方法进行具体的标签解析逻辑。
- 标签解析完成之后还会有一个操作:注册 PostProcessor,spring 会将一些 PostProcessor 类在完成自定义标签解析之后完成 BD 的注册。
注:本文通过源码 + 行说明的方式进行描述,若不好理解可留言。本文仅为个人学习记录,有错误的地方,请大佬们指正。