Spring源码学习(四):component-scan加载beanDefinition

免责声明

本人还处于学习阶段,如果内容有错误麻烦指出,敬请见谅!!!

写在前面

在这里插入图片描述

在这里插入图片描述

1.程序入口

​   loadBeanDefinitions方法会解析XML文件,将其解析成一个文档树(主要是靠BeanDefinitionDocumentReader),继续调用BeanDefinitionDocumentReaderparseBeanDefinitions会遍历这个文档树中的节点,对符合条件的节点进行解析,得到BeanDefinition。

​   所以ApplicationContext委托给XmlBeanDefinitionReaderXmlBeanDefinitionReader又进一步委托给BeanDefinitionDocumentReader

在这里插入图片描述

在这里插入图片描述

2.parseCustomElement干了什么

2.1根据namespaceUri匹配对应的handler和parser

​   首先要知道的是一个XML中会有多个命名空间,每个命名空间下面可能存在多个标签。例如,component-scan位于这个http://www.springframework.org/schema/context命名空间下,该空间也有多个标签。

​   因此我们需要先定位标签的命名空间,再根据命名空间定位这个标签的解析器。

在这里插入图片描述

​   根据namespaceUri获取对应的NameSpaceHandler对象,该实例对象里面也维护了一个XML的标签名到BeanDefinitionParser的mapping,然后再根据XML中的标签名,获取NameSpaceHandler中对应的BeanDefinitionParser

2.1.1获取NameHandler对象实例

​   首先会根据当前标签所处的命名空间去创建对应的命名空间处理器(handler):根据namespaceUri获取对应的NameSpaceHandler对象。

在这里插入图片描述

​   有意思的是这个handlerMappings的Value最开始放的是类的全路径名,首次访问会根据这个全路径名反射得到一个新的handler实例,再将这个handler实例存放到handlerMappings中。

在这里插入图片描述

在这里插入图片描述

2.1.2获取BeanDefinitionParser对象实例

​   然后再根据XML中的标签名,获取NameSpaceHandler中对应的BeanDefinitionParserNameSpaceHandler里面也维护了一个从标签名到标签解析器(parser)的mapping,其中component-scan对应的是ComponentScanBeanDefinitionParser

在这里插入图片描述

2.1.3这一部分的继承关系和调用链是什么

​   调用逻辑大致就是,通过NamespaceHandlerResolver的resolve方法来解析namespaceUri,得到对应的NameSpaceHandler,再根据NameSpaceHandler得到对应的BeanDefinitionParser

​   首先是NamespaceHandlerResolver

在这里插入图片描述

在这里插入图片描述

  然后是NameSpaceHandler

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.2 parse方法干了什么

​   主要的解析工作在于ComponentScanBeanDefinitionParser的parse方法中:

在这里插入图片描述

​   parse的两个参数:element其实是component-scan标签信息的封装,parserContext其实就是之前的BeanDefinitionParserDelegate的对象实例。

在这里插入图片描述

2.2.1 configureScanner:创建并配置一个扫描器

2.2.1.1 createScanner: 创建并初始化一个扫描器

​   创建一个ClassPathBeanDefinitionScanner的对象并返回

在这里插入图片描述

​   ClassPathBeanDefinitionScanner的构造方法如下:

在这里插入图片描述

2.2.1.2 registerDefaultFilters:将一些默认的注解加入到过滤器中

​   在初始化ClassPathBeanDefinitionScanner时会默认加载一些注解到过滤器中,@ManagedBean位于javax.annotation-api依赖中,而@Named位于javax.inject依赖中。一般的项目没有这两个依赖,因此通常来说只会有@Component注解会被加入到过滤器中。

在这里插入图片描述

在这里插入图片描述

为什么要同时设置include和exclude?

例如,当把*@Component注解加入到include中时,Spring除了会去识别@Component外,还会主动去识别@Repository、@Service、@Controller这些使用了@Component*元注解的注解,exclude可以帮我们进一步去筛选这些注解

2.2.1.3 parseTypeFilters:解析过滤器标签exclude-filter和include-filter

​   configureScanner方法在创建完成后,解析XML中,component-scan标签可能配置的过滤器,进一步解析这部分标签

在这里插入图片描述

<context:component-scan base-package="com.spring.source.componentScan">
    <!--exclude-filter,排除扫描被@Component注解标注的类-->
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
</context:component-scan>

2.3.2开始扫描(doScan)

​   configureScanner方法其实干了两件事,首先会创建一个scanner(ClassPathBeanDefinitionScanner),然后配置这个scanner的includeFiltersexcludeFilters。这两件事完成之后,调用doScan方法,开始扫描给定包路径的Bean了。

在这里插入图片描述

2.3.2.1 findCandidateComponents:筛选并找到指定路径下所有符合要求的beanDefinitions

​   首先是调用父类的findCandidateComponents方法:

在这里插入图片描述

在这里插入图片描述

​   继续看看scanCandidateComponents方法,这个也是父类的方法:

在这里插入图片描述

​   可以看到,在获得元数据后会经过两次判断,最终才能确定该类是否符合要求,能否封装成BeanDefinition。

在这里插入图片描述

在这里插入图片描述

​   第一个isCandidateComponent是校验是否满足TypeFilter类型过滤器的要求

在这里插入图片描述

​   第二个isCandidateComponent是校验是否满足bean定义的要求

在这里插入图片描述

​   在这两个判断都通过后,封装好的Bean定义就会加入到集合中,下面是一个BeanDefinition的例子:

在这里插入图片描述

2.3.2.2 generateBeanName:生成BeanName

​   筛选得到的beanDefinition是ScannedGenericBeanDefinition类型的,该类型是AnnotatedBeanDefinition的实现类。generateBeanName主要包括两个步骤,首先会判断类上注解的value是否能作为beanName,如果都不行,则会主动创建一个beanName:

在这里插入图片描述

​   determineBeanNameFromAnnotation方法就是用来获取注解中的value属性:

在这里插入图片描述

在这里插入图片描述

​   如果没有合适的beanName,则会调用buildDefaultBeanName方法,该方法最后的逻辑在Introspectordecapitalize方法中:

在这里插入图片描述

2.3.2.3 简单看看AbstractBeanDefinition和AnnotatedBeanDefinition

​   我们最终得到的beanDefinition是ScannedGenericBeanDefinition类型的,该类型是AnnotatedBeanDefinition的实现类,也是AbstractBeanDefinition的子类。因此会走那两个if逻辑。

在这里插入图片描述

在这里插入图片描述

2.3.2.4 进一步判断Bean是否能注册

在这里插入图片描述

2.3.2.5 注册Bean

​   注册Bean调用的是registerBeanDefinition方法,该方法传入了两个参数,一个是包含beanDefinitiondefinitionHolder,另一个是注册器registry。registry其实是我们在loadBeanDefinitions之前就创建好的空的beanFactory,所以注册Bean最后也是调用beanFactoryregisterBeanDefinition方法。下面是部分beanFactoryregisterBeanDefinition方法,所以注册Bean的本质就是在成员变量beanDefinitionMap中添加beanNamebeanDefinition的映射关系。

在这里插入图片描述

3.总结

1.根据标签的命名空间找到对应的命名空间处理器;
2.根据命名空间处理器找到当前标签的解析器;
3.创建并配置一个扫描器,加入包含和排除哪些注解;
4.找到符合条件的候选Bean;
5.为这些bean生成beanName:首先从在注解中的value属性找,没找到再默认生成一个;
6.进行AnnotatedBeanDefinition和AbstractBeanDefinition的处理;
7.检查beanName的合法性并注册bean,注册其实是将beanName和beanDefinition加入到缓存中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值