我们从SpringBoot的run方法入手,主体逻辑步骤为:

1.prepareEnvironment

2.createApplicationContext

3.prepareContext

4.refreshContext

5.afterRefresh

prepareEnvironment

org.springframework.boot.SpringApplication#run(java.lang.String...) =》org.springframework.boot.SpringApplication#prepareEnvironment =》org.springframework.boot.SpringApplicationRunListeners#environmentPrepared =》org.springframework.boot.context.event.EventPublishingRunListener#environmentPrepared

在这里通过org.springframework.context.event.SimpleApplicationEventMulticaster广播ApplicationEnvironmentPreparedEvent事件。

那么,事件的监听者有哪些?通过代码走读发现,事件的监听者在EventPublishingRunListener构造方法中通过SpringApplication.getListeners()获取。那么,SpringApplication的监听者又是哪来的?

同样,走读代码如下,通过springfactories机制加载org.springframework.context.ApplicationListener实现:

nacos-config 配置数据加载_加载

 这其中就有一个Spring Cloud扩展的实现:

nacos-config 配置数据加载_源码分析_02

 这个listener监听的正是上文提到的环境准备事件ApplicationEnvironmentPreparedEvent:

nacos-config 配置数据加载_ide_03

 org.springframework.cloud.bootstrap.BootstrapApplicationListener#onApplicationEvent主要通过构建临时ApplicationContext,利用Spring自带的IOC能力,查找以SpringFactories方式扩展org.springframework.cloud.bootstrap.BootstrapConfiguration注解的class,BootstrapApplicationListener主要目的是查找ApplicationContextInitializer并加入到SpringApplication的initializers列表中,用于监听后续正式Context事件

代码调用路径:org.springframework.cloud.bootstrap.BootstrapApplicationListener#onApplicationEvent =》 org.springframework.cloud.bootstrap.BootstrapApplicationListener#bootstrapServiceContext

这里通过SpringApplicationBuilder构建临时Context,build的source为org.springframework.cloud.bootstrap.BootstrapImportSelectorConfiguration

nacos-config 配置数据加载_spring_04

 nacos-config 配置数据加载_spring_05

 这里通过org.springframework.cloud.bootstrap.BootstrapImportSelector加载返回字符串对应的class实例到IOC容器。至于@Import注解的分析见另外一篇博文。

@Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        // Use names and ensure unique to protect against duplicates
// 获取通过SpringFactories方式以org.springframework.cloud.bootstrap.BootstrapConfiguration为key扩展的所有class类名
List<String> names = new ArrayList<>(SpringFactoriesLoader .loadFactoryNames(BootstrapConfiguration.class, classLoader));
// 这里有个扩展点,可以通过spring.cloud.bootstrap.sources加载Bootstrap相关bean names.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(
this.environment.getProperty("spring.cloud.bootstrap.sources", "")))); List<OrderedAnnotatedElement> elements = new ArrayList<>(); for (String name : names) { try { elements.add( new OrderedAnnotatedElement(this.metadataReaderFactory, name)); } catch (IOException e) { continue; } } AnnotationAwareOrderComparator.sort(elements); String[] classNames = elements.stream().map(e -> e.name).toArray(String[]::new); return classNames; }

这里有个跟本篇主题相关的Spring Cloud扩展BootstrapConfiguration的两个实现:

nacos-config 配置数据加载_ide_06

 构建完context后回到onApplicationEvent方法,调用org.springframework.cloud.bootstrap.BootstrapApplicationListener#apply。

nacos-config 配置数据加载_加载_07

回到org.springframework.boot.SpringApplication#run(java.lang.String...)方法,

nacos-config 配置数据加载_源码分析_08

 prepareContext

了解spring源码的人应该知道,bean的创建等都是在Context的refresh中。这里再refresh之前做些环境数据的准备工作。

 org.springframework.boot.SpringApplication#prepareContext =》org.springframework.boot.SpringApplication#applyInitializers

循环调用所有initializer的initialize方法:

nacos-config 配置数据加载_ide_09

 看下上文提到的Spring Cloud扩展org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration#initialize,重点代码片段如下,通过propertySourceLocators获取PropertySource:

nacos-config 配置数据加载_spring_10

 问题来了,propertySourceLocators是哪里来的?还记得PropertySourceBootstrapConfiguration是怎么构建的吗?没错,就是以org.springframework.cloud.bootstrap.BootstrapImportSelectorConfiguration为source,创建临时Context加载生成的bean,这里会加载所有以org.springframework.cloud.bootstrap.BootstrapConfiguration为key的bean,我们的主角登场了,Spring Cloud Alibaba Nacos Config扩展了Spring Cloud的这个点:

nacos-config 配置数据加载_源码分析_11

 com.alibaba.cloud.nacos.NacosConfigBootstrapConfiguration自动创建了NacosPropertySourceLocator:

nacos-config 配置数据加载_bootstrap_12

 最终会调用com.alibaba.cloud.nacos.client.NacosPropertySourceLocator#locate:

@Override
    public PropertySource<?> locate(Environment env) {
        nacosConfigProperties.setEnvironment(env);
        ConfigService configService = nacosConfigManager.getConfigService();

        if (null == configService) {
            log.warn("no instance of config service found, can't load config from nacos");
            return null;
        }
        long timeout = nacosConfigProperties.getTimeout();
        nacosPropertySourceBuilder = new NacosPropertySourceBuilder(configService,
                timeout);
        String name = nacosConfigProperties.getName();
        // 优先使用配置的spring.cloud.nacos.config.prefix
        String dataIdPrefix = nacosConfigProperties.getPrefix();
// 如果没有配置prefix,尝试使用spring.cloud.nacos.config.name
if (StringUtils.isEmpty(dataIdPrefix)) { dataIdPrefix = name; } // 还是没有的话,使用spring.application.name if (StringUtils.isEmpty(dataIdPrefix)) { dataIdPrefix = env.getProperty("spring.application.name"); } CompositePropertySource composite = new CompositePropertySource( NACOS_PROPERTY_SOURCE_NAME); // 加载共享配置 loadSharedConfiguration(composite);
// 加载扩展配置 loadExtConfiguration(composite);
// 加载应用配置 loadApplicationConfiguration(composite, dataIdPrefix, nacosConfigProperties, env);
return composite; }

com.alibaba.cloud.nacos.client.NacosPropertySourceLocator#loadApplicationConfiguration =》 com.alibaba.cloud.nacos.client.NacosPropertySourceLocator#loadNacosDataIfPresent =》com.alibaba.cloud.nacos.client.NacosPropertySourceLocator#loadNacosPropertySource =》com.alibaba.cloud.nacos.client.NacosPropertySourceBuilder#build =》com.alibaba.cloud.nacos.client.NacosPropertySourceBuilder#loadNacosData =》com.alibaba.nacos.client.config.NacosConfigService#getConfig

最终通过nacos client的configService从服务端获取数据并封装成PropertySource返回。