一、SpringBoot配置文件的加载

主要研究SpringApllication.run() 方法中 prepareEnvironment() 方法

public ConfigurableApplicationContext run(String... args) {
		
		try {
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
        }
}

1. prepareEnvironment()

private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
    // 创建对应的环境根据应用程序的类型
		ConfigurableEnvironment environment = getOrCreateEnvironment();
    //获取到启动的参数,通过参数配置环境
		configureEnvironment(environment, applicationArguments.getSourceArgs());
    //发布环境准备的事件(重点方法)
		listeners.environmentPrepared(environment);
    //将环境绑定到应用程序当中
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader())
					.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}
2. getOrCreateEnvironment()

根据 this.webApplicationType 会创建 StandardServletEnvironment 对象;会注册 4个配置对象到对象的List中

  • servletConfigInitParams:空对象
  • servletContextInitParams:空对象
  • systemProperties:应用程序启动的相关配置
  • systemEnvironment:环境遍历相关的配置

spring Environment需要引入哪个依赖 spring environment类_配置文件

private ConfigurableEnvironment getOrCreateEnvironment() {
    if (this.environment != null) {
        return this.environment;
    }
    //根据应用的类型选择环境,这里会创建一个 StandardServletEnvironment 对象
    switch (this.webApplicationType) {
        case SERVLET:
            return new StandardServletEnvironment();
        case REACTIVE:
            return new StandardReactiveWebEnvironment();
        default:
            return new StandardEnvironment();
    }
}
3. configureEnvironment()

配置环境的一些配置信息,设置转换器用作参数的转换

protected void configureEnvironment(ConfigurableEnvironment environment,
			String[] args) {
		if (this.addConversionService) {
			ConversionService conversionService = ApplicationConversionService
					.getSharedInstance();
			environment.setConversionService(
					(ConfigurableConversionService) conversionService);
		}
    	//设置默认的配置文件类,根据传入的参数注入指定的配置信息
		configurePropertySources(environment, args);
    	//设置配置的环境
		configureProfiles(environment, args);
	}
4. environmentPrepared()

发布环境配置的时间,这是重点方法,根据环境读取到对应的配置文件;

//通过事件广播器,发布应用环境准备的事件
public void environmentPrepared(ConfigurableEnvironment environment) {
		this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
				this.application, this.args, environment));
	}

ResolvableType:其中包括了事件的类型,父类类型,以及实现的接口,还有泛型类型

public void multicastEvent(ApplicationEvent event) {
    //第一个参数是事件对象,第二个参数,根据事件解析出对应的事件类型对象(ResolvableType)
        this.multicastEvent(event, this.resolveDefaultEventType(event));
    }
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
    //获取到对应的监听器,其中使用了缓存的方式将对应的事件类型跟监听器缓存起来,便于后续获取;其中使用了 GenericApplicationListenerAdapter 适配器的方式来提供给不同的监听器用来进行适配
        Iterator var4 = this.getApplicationListeners(event, type).iterator();

        while(var4.hasNext()) {
            ApplicationListener<?> listener = (ApplicationListener)var4.next();
            Executor executor = this.getTaskExecutor();
            if (executor != null) {
                //如果设置了线程池,就使用异步的方式进行执行
                executor.execute(() -> {
                    this.invokeListener(listener, event);
                });
            } else {
                //执行监听器执行的监听器为:ConfigFileApplicationListener
                this.invokeListener(listener, event);
            }
        }

    }

2. ConfigFileApplicationListener

@Override
	public void onApplicationEvent(ApplicationEvent event) {
        //根据事件的类型执行相应的方法
		if (event instanceof ApplicationEnvironmentPreparedEvent) {
			onApplicationEnvironmentPreparedEvent(
					(ApplicationEnvironmentPreparedEvent) event);
		}
		if (event instanceof ApplicationPreparedEvent) {
			onApplicationPreparedEvent(event);
		}
	}
1. onApplicationEnvironmentPreparedEvent

通过SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class,getClass().getClassLoader()); 的方法找到 spring.factories 中 EnvironmentPostProcessor 的实现类执行处理,目前加载出来的对象如下图:

spring Environment需要引入哪个依赖 spring environment类_springboot_02

其中还会将ConfigFileApplicationListener 也添加到处理器当中进行处理可以看到当前类也实现了 EnvironmentPostProcessor

private void onApplicationEnvironmentPreparedEvent(
			ApplicationEnvironmentPreparedEvent event) {
    //加载 EnvironmentPostProcessor 的实现类
		List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
    //将 ConfigFileApplicationListener 添加到处理链当中。配置文件的读取也是由 ConfigFileApplicationListener进行处理
		postProcessors.add(this);
		AnnotationAwareOrderComparator.sort(postProcessors);
		for (EnvironmentPostProcessor postProcessor : postProcessors) {
			postProcessor.postProcessEnvironment(event.getEnvironment(),
					event.getSpringApplication());
		}
	}
2. postProcessEnvironment

当前方法调用了内部方法 addPropertySources(environment, application.getResourceLoader());

3. addPropertySources()

其中创建了 Loder对象,,并且调用了load() 方法

new Loader(environment, resourceLoader).load();

4. load()
public void load() {
			this.profiles = new LinkedList<>();
			this.processedProfiles = new LinkedList<>();
			this.activatedProfiles = false;
			this.loaded = new LinkedHashMap<>();
    //初始化环境。会将环境中的默认环境添加当 this.profiles 当中,初始会有两个元素一个是空的,一个"default"
			initializeProfiles();
			while (!this.profiles.isEmpty()) {
       			//取出环境进行处理
				Profile profile = this.profiles.poll();
				if (profile != null && !profile.isDefaultProfile()) {
					addProfileToEnvironment(profile.getName());
				}
                //加载路径下的配置文件
				load(profile, this::getPositiveProfileFilter,
                     //加载文件后将数据添加到环境配置中
						addToLoaded(MutablePropertySources::addLast, false));
				this.processedProfiles.add(profile);
			}
    //重置环境中的配置环境,设置使用的环境
			resetEnvironmentProfiles(this.processedProfiles);
			load(null, this::getNegativeProfileFilter,
					addToLoaded(MutablePropertySources::addFirst, true));
    //文件加载完成之后就直接添加到环境当中
			addLoadedPropertySources();
		}

初始时的环境配置

spring Environment需要引入哪个依赖 spring environment类_配置文件_03

private void load(Profile profile, DocumentFilterFactory filterFactory,
				DocumentConsumer consumer) {
    //获取到所以的配置文件的路径:初始时有4个文件的路径
			getSearchLocations().forEach((location) -> {
				boolean isFolder = location.endsWith("/");
                //这里拼接对应配置路径
				Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
                //遍历处理
				names.forEach(
						(name) -> load(location, name, profile, filterFactory, consumer));
			});
		}


private void load(String location, String name, Profile profile,
				DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
			if (!StringUtils.hasText(name)) {
				for (PropertySourceLoader loader : this.propertySourceLoaders) {
					if (canLoadFileExtension(loader, location)) {
						load(loader, location, profile,
								filterFactory.getDocumentFilter(profile), consumer);
						return;
					}
				}
			}
    // propertySourceLoaders : 会有两个处理类 YamlPropertySourceLoader和PropertiesPropertySourceLoader
			Set<String> processed = new HashSet<>();
    //获取到处理类
			for (PropertySourceLoader loader : this.propertySourceLoaders) {
                //处理类支持处理的文件后缀yml
				for (String fileExtension : loader.getFileExtensions()) {
					if (processed.add(fileExtension)) {
                        //加载对应路径的配置文件信息
						loadForFileExtension(loader, location + name, "." + fileExtension,
								profile, filterFactory, consumer);
					}
				}
			}
		}

spring Environment需要引入哪个依赖 spring environment类_java_04

spring Environment需要引入哪个依赖 spring environment类_java_05

最后根据路径和环境拼接出配置文件的路径信息

spring Environment需要引入哪个依赖 spring environment类_bootstrap_06

最后添加到环境当中

spring Environment需要引入哪个依赖 spring environment类_springboot_07

二、SpringCloud配置文件的加载

探究Nacos是如何在springboot启动时加载到对应的配置文件nacos 加载配置时需要知道以下几个类

  • NacosPropertySourceLocator:配置读取器
  • NacosConfigProperties:指定的配置对象
  • NacosConfigManager:配置管理器
  • ConfigService:配置服务
  • PropertySourceBootstrapConfiguration:spring cloud提供的统一配置属性bootstrap装配类
  • NacosConfigBootstrapConfiguration:nacos提供的bootstrap环境装配类

1. bootstrap

首先我们知道 spring cloud 微服务在启动时优先读取的是 bootstrap.yml 的文件,因为这个文件的优先级最高,为什么最高呢?我们可以看一看启动的代码。下面是 SpringApplication.run() 里面调用环境准备的方法

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
    //配置以下需要启动的环境,这里面实际配置是 this.additionalProfiles 这个默认的环境
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		ConfigurationPropertySources.attach(environment);
    //发布一个环境准备完毕的时间
		listeners.environmentPrepared(environment);
    //将环境对象和上下文绑定起来
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

注意:重点在 environmentPrepared() 这个发布事件中,引入了spring cloud时会有一个类叫 BootstrapApplicationListener;这个类我们就叫做 bootstrap 应用容器

public class BootstrapApplicationListener
		implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {

@Override
	public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
		//获取到容器的配置环境对象
		ConfigurableEnvironment environment = event.getEnvironment();
		//判断是否开启全局上下文的选项 spring.cloud.bootstrap.enabled
		if (!environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class,
				true)) {
			return;
		}
		// 判断一下环境中是否已经存在名称叫 bootstrap 的配置文件
		if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
			return;
		}
		ConfigurableApplicationContext context = null;
		//读取配置文件的名称,这里获取环境里面有没有指定bootstrap的名称,默认就为 bootstrap,这里也对应上了上面
		String configName = environment
				.resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");
		//从spring容器中读取出上下文初始化器执行
		for (ApplicationContextInitializer<?> initializer : event.getSpringApplication()
				.getInitializers()) {
			//只执行 ParentContextApplicationContextInitializer 的类型
			if (initializer instanceof ParentContextApplicationContextInitializer) {
				context = findBootstrapContext(
						(ParentContextApplicationContextInitializer) initializer,
						configName);
			}
		}
		//如果没用 ParentContextApplicationContextInitializer 类型的执行器,这里上下文对象则是空的
		if (context == null) {
			/**
			 * 构建一个上下文对象,注意:这里就是导致spring boot引入spring cloud时启动了两次容器,全局上下文的容器在这里也会单独进行一次的创建,但是这里面只会
			 * 将 BootstrapConfiguration 类型的配置类进行加载,其他的配置类不会进行加载,例如:nacos中的配置信息加载
			 */
			context = bootstrapServiceContext(environment, event.getSpringApplication(),
					configName);
			event.getSpringApplication()
					.addListeners(new CloseContextOnFailureApplicationListener(context));
		}
		apply(context, event.getSpringApplication(), environment);
	}
    
    
    
    //创建一个子容器用于加载bootstrap相关的配置
private ConfigurableApplicationContext bootstrapServiceContext(
			ConfigurableEnvironment environment, final SpringApplication application,
			String configName) {
		//创建一个标准环境对象
		StandardEnvironment bootstrapEnvironment = new StandardEnvironment();
		MutablePropertySources bootstrapProperties = bootstrapEnvironment
				.getPropertySources();
		//将配置都置空
		for (PropertySource<?> source : bootstrapProperties) {
			bootstrapProperties.remove(source.getName());
		}
		//获取到 bootstrap的配置文件的路径,因为这是去从主流程的容器中获取,里面都没有配置所以都读取出来的是空的
		String configLocation = environment
				.resolvePlaceholders("${spring.cloud.bootstrap.location:}");
		String configAdditionalLocation = environment
				.resolvePlaceholders("${spring.cloud.bootstrap.additional-location:}");
		Map<String, Object> bootstrapMap = new HashMap<>();
		//将配置文件的名称放入到 bootstrapMap 中 这个 configName 默认就是 bootstrap
		bootstrapMap.put("spring.config.name", configName);
		bootstrapMap.put("spring.main.web-application-type", "none");
		if (StringUtils.hasText(configLocation)) {
			bootstrapMap.put("spring.config.location", configLocation);
		}
		if (StringUtils.hasText(configAdditionalLocation)) {
			bootstrapMap.put("spring.config.additional-location",
					configAdditionalLocation);
		}
		//在环境配置文件的第一个位置添加一个名称为 bootstrap 的配置文件,这是一个临时配置文件对象
		bootstrapProperties.addFirst(
				new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap));
		//这里是获取到外部传入的环境对象,将两个配置文件进行合并一下
		for (PropertySource<?> source : environment.getPropertySources()) {
			if (source instanceof StubPropertySource) {
				continue;
			}
			bootstrapProperties.addLast(source);
		}
		/**
		 * 构建一个 SpringApplication 的构建器出来,这里面构建时并没有传入主要的启动类
		 */
		SpringApplicationBuilder builder = new SpringApplicationBuilder()
				.profiles(environment.getActiveProfiles()).bannerMode(Mode.OFF)
				.environment(bootstrapEnvironment)
				.registerShutdownHook(false).logStartupInfo(false)
				.web(WebApplicationType.NONE);
		final SpringApplication builderApplication = builder.application();
		//从调用栈获取出的main方法的类,这里面如果没有解析出main方法的类,那么就默认会取父级容器的main方法的类
		if (builderApplication.getMainApplicationClass() == null) {
			/**
			 * 从spring boot的启动类中获取到main方法的类
			 */
			builder.main(application.getMainApplicationClass());
		}
		if (environment.getPropertySources().contains("refreshArgs")) {
			/**
			 * 将当前启动的普通容器设置到新的容器中
			 */
			builderApplication
					.setListeners(filterListeners(builderApplication.getListeners()));
		}
		/**
		 * 添加 BootstrapImportSelectorConfiguration 类到容器中,这个类作为自动装配类会优先加载
		 * spring.factories文件中 BootstrapConfiguration.class 标记的类,而这个配置类中又会添加到
		 * SpringApplication中的 primarySources 中,所以会优先加载,例如:NacosPropertySourceLocator 配置加载器就需要在这里面进行
		 * 加载优先级更高。SpringApplication.prepareContext() 方法中就会将 primarySources中的类加载到容器中
		 */
		builder.sources(BootstrapImportSelectorConfiguration.class);
		//开器上下文的执行
		final ConfigurableApplicationContext context = builder.run();
		context.setId("bootstrap");
		addAncestorInitializer(application, context);
		//将 bootstrap 临时文件移除
		bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME);
		//合并两个配置文件,实际上就是将上面创建的bootstrap加载的配置文件合并到外部传入的配置文件中
		mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties);
		return context;
	}

}

bootstrapServiceContext() 这个方法呢目的是创建一个 bootstrap子容器 主要的目的是加载 BootstrapImportSelectorConfiguration 这个配置类中需要提前导入加载的类,这个类会去 spring.factories 中指定的下面的配置;

org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.alibaba.cloud.nacos.NacosConfigBootstrapConfiguration

bootstrapServiceContext() 方法里面构建了一个 bootstrap 的配置文件对象设置到了对应的子环境中,下面这段代码配置了容器需要加载的配置文件名称 spring.config.name,这个属性就是在 ConfigFileApplicationListener 里面会进行读取需要配置的名称

Map<String, Object> bootstrapMap = new HashMap<>();
//将配置文件的名称放入到 bootstrapMap 中 这个 configName 默认就是 bootstrap
bootstrapMap.put("spring.config.name", configName);
bootstrapMap.put("spring.main.web-application-type", "none");
if (StringUtils.hasText(configLocation)) {
    bootstrapMap.put("spring.config.location", configLocation);
}
if (StringUtils.hasText(configAdditionalLocation)) {
    bootstrapMap.put("spring.config.additional-location",
                     configAdditionalLocation);
}

2. PropertySourceBootstrapConfiguration

上面讲完了 bootstrap.yml 文件的加载,当我们启动了一个 bootstrap容器 之后,会去默认加载 BootstrapConfiguration 下面所有的装配类

org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration,\

这个类是 spring cloud提供的通用类,下面会注入所有的 PropertySourceLocator ,然后在 initialize() 方法中进行调用;

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(PropertySourceBootstrapProperties.class)
public class PropertySourceBootstrapConfiguration implements
		ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
    
    /**
	 * 自定注入所有的实现类 PropertySourceLocator
	 */
	@Autowired(required = false)
	private List<PropertySourceLocator> propertySourceLocators = new ArrayList<>();
    
    @Override
	public void initialize(ConfigurableApplicationContext applicationContext) {
		//将nacos中的配置构建为 PropertySource 配置属性对象
		List<PropertySource<?>> composite = new ArrayList<>();
		//根据@Order注解进行排序
		AnnotationAwareOrderComparator.sort(this.propertySourceLocators);
		boolean empty = true;
		//获取到环境对象
		ConfigurableEnvironment environment = applicationContext.getEnvironment();
		//循环遍历
		for (PropertySourceLocator locator : this.propertySourceLocators) {
			//调用实现方法进行构建配置对象 PropertySource
			Collection<PropertySource<?>> source = locator.locateCollection(environment);
			//如果配置出来的 PropertySource 为空,那么直接跳过到下一个
			if (source == null || source.size() == 0) {
				continue;
			}
			List<PropertySource<?>> sourceList = new ArrayList<>();
			//根据配置出来的 PropertySource 在进行包装,除了 EnumerablePropertySource枚举类型配置源,其他的都是 SimpleBootstrapPropertySource
			for (PropertySource<?> p : source) {
				if (p instanceof EnumerablePropertySource) {
					EnumerablePropertySource<?> enumerable = (EnumerablePropertySource<?>) p;
					sourceList.add(new BootstrapPropertySource<>(enumerable));
				}
				else {
					sourceList.add(new SimpleBootstrapPropertySource(p));
				}
			}
			logger.info("Located property source: " + sourceList);
			composite.addAll(sourceList);
			empty = false;
		}
		//如果配置部位空的话
		if (!empty) {
			MutablePropertySources propertySources = environment.getPropertySources();
			//解析日志配置的占位符
			String logConfig = environment.resolvePlaceholders("${logging.config:}");
			LogFile logFile = LogFile.get(environment);
			for (PropertySource<?> p : environment.getPropertySources()) {
				if (p.getName().startsWith(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
					propertySources.remove(p.getName());
				}
			}
			insertPropertySources(propertySources, composite);
			reinitializeLoggingSystem(environment, logConfig, logFile);
			setLogLevels(applicationContext, environment);
			handleIncludedProfiles(environment);
		}
	}
}

这里需要说明以下 bootstrap 的加载跟主流程的容器加载是分开的,bootstrap 加载完之后,会将配置文件和 ApplicationContextInitializer 跟主流程的容器进行合并,所以这就是为什么 bootstrap 的文件优先级最高

3. NacosConfigBootstrapConfiguration

这个配置类主要是注入了 NacosPropertySourceLocatorNacosConfigManagerNacosConfigProperties 三个bean对象,主要是查看 NacosPropertySourceLocator

4. NacosPropertySourceLocator

这个类就是通过 ConfigService 去根据配置的应用名称、命名空间等去拉取配置

public class NacosPropertySourceLocator implements PropertySourceLocator {
    private NacosConfigManager nacosConfigManager;
    
    @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();

        //获取到配置的前缀
		String dataIdPrefix = nacosConfigProperties.getPrefix();
		if (StringUtils.isEmpty(dataIdPrefix)) {
			dataIdPrefix = name;
		}

        //如果配置都为空,那么直接获取 spring.application.name 配置的名称
		if (StringUtils.isEmpty(dataIdPrefix)) {
			dataIdPrefix = env.getProperty("spring.application.name");
		}

        //创建一个配置名称为 nacos 的聚合配置属性对象
		CompositePropertySource composite = new CompositePropertySource(
				NACOS_PROPERTY_SOURCE_NAME);
        //加载共享配置文件,分组为默认
		loadSharedConfiguration(composite);
        //加载扩展配置文件
		loadExtConfiguration(composite);
        //加载配置文件
		loadApplicationConfiguration(composite, dataIdPrefix, nacosConfigProperties, env);
		return composite;
	}
    
    
    private void loadApplicationConfiguration(
			CompositePropertySource compositePropertySource, String dataIdPrefix,
			NacosConfigProperties properties, Environment environment) {
        //获取到指定的文件后缀,默认是properties
		String fileExtension = properties.getFileExtension();
        //获取到配置的分组
		String nacosGroup = properties.getGroup();
		// 加载没有带后缀的配置文件例如:product 因为这里直接将服务名称作为了dataId
		loadNacosDataIfPresent(compositePropertySource, dataIdPrefix, nacosGroup,
				fileExtension, true);
		// 加载带后缀的配置文件例如:product.properties
		loadNacosDataIfPresent(compositePropertySource,
				dataIdPrefix + DOT + fileExtension, nacosGroup, fileExtension, true);
		// 根据激活的环境去拼接对应的配置文件名称并且进行加载
		for (String profile : environment.getActiveProfiles()) {
			String dataId = dataIdPrefix + SEP1 + profile + DOT + fileExtension;
            /**
             * 1. 先通过 NacosPropertySourceRepository 判断配置文件是否已经加载了
             * 2. 在 nacosPropertySourceBuilder 通过 configService 加载配置文件,加载完成了后会将其注册到 NacosPropertySourceRepository
             * 3. 在上下文创建完成后根据配置文件再注册对应的监听器 NacosContextRefresher
             */
			loadNacosDataIfPresent(compositePropertySource, dataId, nacosGroup,
					fileExtension, true);
		}

	}
}

5. NacosConfigService

nacos提供的配置服务,这个配置服务就是通过 http请求加线程池轮询去监听配置变动(2.0之前)

public class NacosConfigService implements ConfigService {
    
    //http代理服务,发起http请求的
    private final HttpAgent agent;
    
    //用于轮询读取远程配置的变动并且通知注册的监听器
    private final ClientWorker worker;
    
    
    public NacosConfigService(Properties properties) throws NacosException {
        //严重参数
        ValidatorUtils.checkInitParam(properties);
        //初始化命名空间的名称
        initNamespace(properties);
        //通过 ServiceLoader 加载出来的 ConfigFilter 实现类,然后将其添加到 ConfigFilterChainManager 中
        this.configFilterChainManager = new ConfigFilterChainManager(properties);
        //启动服务端列表管理器
        ServerListManager serverListManager = new ServerListManager(properties);
        serverListManager.start();
        /**
         * 启动本地配置信息处理器,2.0之前采用的http长轮询的方式进行监听,2.0之后采用rpc的方式进行监听
         * 通过一个阻塞队列进行线程的阻塞,默认是5秒钟便会唤醒一次,然后进行一次配置变动的监听更新,如果
         * 有监听器进行注册,那么会立即执行一次
         */
        this.worker = new ClientWorker(this.configFilterChainManager, serverListManager, properties);
        // will be deleted in 2.0 later versions
        agent = new ServerHttpAgent(serverListManager);

    }
    
    public String getConfig(String dataId, String group, long timeoutMs) throws NacosException {
        return this.getConfigInner(this.namespace, dataId, group, timeoutMs);
    }

    public String getConfigAndSignListener(String dataId, String group, long timeoutMs, Listener listener) throws NacosException {
        String content = this.getConfig(dataId, group, timeoutMs);
        this.worker.addTenantListenersWithContent(dataId, group, content, Arrays.asList(listener));
        return content;
    }

    public void addListener(String dataId, String group, Listener listener) throws NacosException {
        this.worker.addTenantListeners(dataId, group, Arrays.asList(listener));
    }

    public boolean publishConfig(String dataId, String group, String content) throws NacosException {
        return this.publishConfig(dataId, group, content, ConfigType.getDefaultType().getType());
    }

    public boolean publishConfig(String dataId, String group, String content, String type) throws NacosException {
        return this.publishConfigInner(this.namespace, dataId, group, (String)null, (String)null, (String)null, content, type);
    }

    public boolean removeConfig(String dataId, String group) throws NacosException {
        return this.removeConfigInner(this.namespace, dataId, group, (String)null);
    }

    public void removeListener(String dataId, String group, Listener listener) {
        this.worker.removeTenantListener(dataId, group, listener);
    }
    
}

6. 总结

nacos读取配置的流程就是,引入spring cloud依赖,在启动容器时,发布了一个 BootstrapApplicationListener 事件,启动一个 bootstrap 容器来读取 spring.factoriesorg.springframework.cloud.bootstrap.BootstrapConfiguration 定义的装配类,就会加载到 NacosConfigBootstrapConfiguration、PropertySourceBootstrapConfiguration 两个装配类,而 PropertySourceBootstrapConfiguration 会注入所有的 PropertySourceLocator 类来进行加载配置。