一、多轮循环解析
在《配置类解析(中)》篇,介绍了解析配置类的核心流程,主要是介绍了parese()方法中的流程,那么在该方法的do-while循环中,还有哪些部分呢,这里将重点介绍
刚开始进行配置类解析时,获取到的配置类只是一小部分,更多的配置类是在解析过程中新生成,而新生成的配置类,一部分在当前轮解析的时候就已经解析过了,但还有一部分却没有解析。比如第二部分中介绍到的添加到当前配置类属性中的实例或资源,其中有importBeanDefinitionRegistrars、importedResources和BeanMethod,解析前两者时都有可能会生成新的配置类,尤其是importedResources
do {
StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
// 解析配置类,会把每个BeanDefinitionHolder首先封装为ConfigurationClass
// 在这个过程中会进行扫描、导入等步骤,从而会找到其他的ConfigurationClass
// 解析配置类的结果是什么?
parser.parse(candidates); // AppConfig.class--->BeanDefinition
……
}
while (!candidates.isEmpty());
1.1 加载配置类的BeanDefinition
首先获取所有已经解析过的配置类,这些配置类中可能就有上面三个属性的值,所以会去调用loadBeanDefinitions()方法去生成这些配置类对应的一些BeanDefinition,对于处理过的配置类,添加到alreadyParsed缓存中,表示这些配置类已经完全处理过了
// configClasses相当于就是解析之后的结果
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
// 把所有的ConfigurationClass加载成BeanDefinition,通过情况下一个配置类会对应一个BeanDefinition,不过也有可能一个配置类对应多个BeanDefinition
// 比如一个配置类中有多个@Bean,一个配置配置了@ImportResource
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();
// candidates中存的是BeanDefinition,configClasses中存的是ConfigurationClass
candidates.clear();
遍历这些配置类
-
如果该配置类是通过Import导入的或者是@Component类的内部类,都经过了解析,但还没生成BeanDefinition,在这里为这些配置类注册BeanDefinition
-
遍历当前配置类的BeanMethod实例列表,为@Bean注解方法生成BeanDefinition,在生成BeanDefinition的过程中,如果已经有该类型的BeanDefinition(方法重载)则不会再去重复生成,但需要把之前生成的BeanDefinition的isFactoryMethodUnique设置为false,表示有重载方法,当getBean()的时候,会根据这个标识去选择合适的方法创建Bean实例
-
如果配置类通过@ImportResource引入了spring.xml文件,则在这里去解析xml文件,在这个过程中,可能会生成新的配置类的BeanDefinition
-
最后去遍历实现了ImportBeanDefinitionRegistrar接口的实例,调用其registerBeanDefinitions()方法,这里也可能会生成新的配置类的BeanDefinition
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
……
if (configClass.isImported()) {
// 将被导入的类生成BeanDefinition并注册到Spring容器中
// @Component的内部类,@Import所导入的类都是被导入的类
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
// @Bean生成BeanDefinition并注册
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
// 处理@ImportResource("spring.xml")
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
// 处理ImportBeanDefinitionRegistrar,调用registerBeanDefinitions()方法
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
1.2 解析新注册的BeanDefinition的配置类
在加载配置类的BeanDefinition时,生成的新的BeanDefinition可能是配置类,检查BeanDefinition是否是配置类,并且没有经过解析,加入到candidates中,循环进行解析
do {
StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
// 解析配置类,会把每个BeanDefinitionHolder首先封装为ConfigurationClass
// 在这个过程中会进行扫描、导入等步骤,从而会找到其他的ConfigurationClass
// 解析配置类的结果是什么?
parser.parse(candidates); // AppConfig.class--->BeanDefinition
……
// 如果发现BeanDefinition增加了,则有可能增加了配置类
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
// 检查多出来的BeanDefinition是不是配置类,需不需要解析
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty());
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/112152.html