Spring配置类解析(下)

导读:本篇文章讲解 Spring配置类解析(下),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

一、多轮循环解析

《配置类解析(中)》篇,介绍了解析配置类的核心流程,主要是介绍了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

(0)
Java光头强的头像Java光头强

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!