【Spring源码系列- IOC】
目录
processConfigBeanDefinitions()
之前的文章捋了捋PostProcessor的处理流程,其中有一个特别重要的PostProcessor:ConfigurationClassPostProcessor,即配置类的后置处理器(参与BeanFactory的建造) 主要功能:
-
解析被@Configuration修饰的配置类
-
解析@ComponentScan扫描的包
-
解析@ComponentScans扫描的包
-
解析被@Import注解修饰的类
入口在哪里
首先的问题是,我们是在什么时候第一次进入这个类的? 上一篇我们捋了捋invokeBeanFactoryPostProcessors()的整体逻辑,由于ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor所以他会在invokeBeanFactoryPostProcessors()方法中被处理,我们debug到invokeBeanFactoryPostProcessors()方法中
ConfigurationClassPostProcessor是在第二步(处理子类,详见上一篇文章)中,即处理实现了BeanDefinitionRegistryPostProcessor接口的步骤中被处理的,而且发现有一点就是我刚开始使用配置文件进行配置的时候,整个debuginvokeBeanFactoryPostProcessors()方法的过程中都没有看到ConfigurationClassPostProcessor这个类,当我使用配置类时,才在上图的位置发现了ConfigurationClassPostProcessor 继续执行,进入对子类单独处理的遍历中
在这里会进入到ConfigurationClassPostProcessor中实现的postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) 方法
这里会先生成一个registryId,用做容器中Bean的唯一标识,然后判断这个传入的registry是否已经被处理过,处理过,抛出异常,未被处理过,则将其registryId添加进已经处理的集合对象中,之后就会进入核心处理环节,如下图
processConfigBeanDefinitions()
筛选Bean
进入该方法后,先创建了一个用于存放BeanDefinitionHolder的对象集合List<BeanDefinitionHolder> configCandidates,获取当前传入的registry(此处为DefaultListableBeanFactory)中所有已经注册的BeanDefinition的beanName,紧接着遍历这些BeanDefinition的beanName,筛选出被特定注解修饰的bean
上图中的遍历筛选部分的代码
for (String beanName : candidateNames) {
/** 获取制定名称的beanDefinition对象 */
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory));
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
上面的for循环,依次获取BeanDefinition,并进行两个判断:
-
是否包含属性ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE)
2. 判断当前BeanDefinition是否是一个配置类,并根据注解类型为BeanDefinition设置属性属性(以便后续进行调用),这个主要逻辑在checkConfigurationClassCandidate()方法中,下一篇(checkConfigurationClassCandidate )会详细介绍这个函数,它里面的主要逻辑是:
-
设置为full/lite
-
如果被@Configuration修饰则为full
-
如果被@Bean、@Component、@ComponentScan、@Import、@ImportResoure修饰则属性值设为lite
-
-
如果配置类上被@order修饰,则为BeanDefinition添加order参数
遍历判断后,如果未发现有配置相关的类,则直接返回
对添加@Order注解的类,进行排序操作
继续判断当前类型是否是SingletonBeanRegistry类型(进行BeanName生成器的配置)
解析配置类
之后就到了重要的一步:解析配置类 先实例化ConfigurationClassParser类+初始化相关参数(如下图中注释) 之后进入配置类的解析工作parser.parse()
此处会解析带有@ComponentScan、@ComponentScans、@Import、@ImportResource、@Bean、@Controller的BeanDefinition 但有一点需要注意,这里只是把两种类(添加了@Configuration注解的类和通过ComponentScan注解扫描的类)加入到BeanDefinitionMap中,其余的方式(@Import注册的类、@Bean方法定义的类)在parse()这一步不会被解析为BeanDefinition放进BeanDefinitionMap中(实际在this.reader.loadBeanDefinitions()中实现,只是发现流程是这样,欢迎知道原因的同学踢踢我(。ì _ í。)抱拳多谢.gif)
之后就进入了递归调用processConfigurationClass()方法来处理配置类的内部类所带注解、内部类的内部类所带注解(如果有的话)……()详见文章递归调用的processConfigurationClass()方法
校验配置类
校验已解析完的配置类,主要校验两个部分:
-
配置类不能被final修饰
-
@Bean修饰的方法必须可以重写以支持CGLIB
拓展:SpringBoot的自动装配是如何实现的?
处理BeanFactoryPostProcessor时有一个特别重要的PostProcessor:ConfigurationClassPostProcessor,主要用来处理相关注解的解析工作(包括@Component、@ComponentScan、@PropertySource、@Bean、@Import等) 在解析@Import时,会从启动类开始,挨个查找,查找过程中会识别到一个AutoConfigurationImportSelector的类,在解析这个类时,有一个延迟加载的属性,再延迟加载时会通过getImports()方法,获取一个AutoConfigurationEntry对象,会调用里面的getCandidateConfigurations()方法加载配置文件(spring.factories)中的属性值
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/135412.html