配置
maven配置
<!--version 2022.0.4-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--version 3.0.9-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--version 2022.0.0-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
yaml配置
spring:
application:
name: nacos-demo
cloud:
nacos:
server-addr: localhost:8848
config:
import:
## nacos-config是nacos里配置文件的dataId,和以前的配置有所区别,不需要加上文件后缀
## 可以配置多个文件,相同的配置项,后面的会覆盖前面的
- nacos:nacos-config
由于springboot已经在2.4.0版本默认关闭了bootstrap.yaml功能,改用spring.config.import来配置要加载的各种来源的配置文件。旧项目升级版本时,可以通过引入spring-cloud-starter-bootstrap依赖,或者设定环境变量spring.cloud.bootstrap.enabled=true,来继续使用bootstrap.yaml
获取nacos配置原理
启动时加载nacos配置
我们知道在springboot启动的时候,在容器刷新前,会从各个数据源里获取到配置信息,常见的有系统环境变量,application.yaml,web项目还有servletConfig和servletContext。显然nacos里的配置信息也会在这里注入进去。核心逻辑在SpringApplication#prepareEnvironment()
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(bootstrapContext, environment);
// 省略部分非关键代码
return environment;
}
通过debug watch environment对象的propertySources属性,发现在listeners.environmentPrepared方法执行后,获取到了nacos里的配置信息。这句代码实际就是在创建environment对象,并添加了几个本地配置源后,广播了环境准备就绪的事件。显然核心逻辑在监听了environmentPrepared事件的监听器里。environmentPrepared事件属于springboot 6个生命周期事件之一,由SpringApplicationRunlistener的实现类来监听,nacos的NacosLoggingAppRunListener实现了该接口。但是点进去发现该监听器并没有对environment做任何的修改。
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
NacosLogging.getInstance().loadConfiguration();
}
逻辑只能在另一个SpringApplicationRunlistener的实现类EventPublishingRunListener里,该类将生命周期事件进行了一次广播,广播给了ApplicationListener的实现类.
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
ConfigurableEnvironment environment) {
multicastInitialEvent(
new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));
}
通过debug发现,在ApplicationListener的实现类EnvironmentPostProcessorApplicationListener进行时间处理后,获取到了参数值,该类则是遍历执行了EnvironmentPostProcessor。
发现其中有一个后置处理器COnfigDataEnvironmentPostProcessor,该类完成了从本地配置文件application.yaml以及从spring.config.import配置里获取的其他数据源配置文件的配置读取。
其中从nacos远程获取配置文件配置的逻辑由NacosConfigDataLoader完成,它从上下文里获取到了nacos配置项,然后调用了pullConfig方法来请求nacos里的配置。最终的实际请求由ClientWorker类进行
public ConfigData doLoad(ConfigDataLoaderContext context, NacosConfigDataResource resource) {
try {
ConfigService configService = ((NacosConfigManager)this.getBean(context, NacosConfigManager.class)).getConfigService();
NacosConfigProperties properties = (NacosConfigProperties)this.getBean(context, NacosConfigProperties.class);
NacosConfigDataResource.NacosItemConfig config = resource.getConfig();
List<PropertySource<?>> propertySources = this.pullConfig(configService, config.getGroup(), config.getDataId(), config.getSuffix(), (long)properties.getTimeout());
NacosPropertySource propertySource = new NacosPropertySource(propertySources, config.getGroup(), config.getDataId(), new Date(), config.isRefreshEnabled());
NacosPropertySourceRepository.collectNacosPropertySource(propertySource);
return new ConfigData(propertySources, this.getOptions(context, resource));
} catch (Exception var8) {
this.log.error("Error getting properties from nacos: " + resource, var8);
if (!resource.isOptional()) {
throw new ConfigDataResourceNotFoundException(resource, var8);
} else {
return null;
}
}
}
因此,最终的事件通知链是:
- 发布environmentPrepared事件
- EventPublishingRunListener广播事件给EnvironmentPostProcessorApplicationListener
- EnvironmentPostProcessorApplicationListener遍历EnvironmentPostProcessor后置处理器进行后置处理
- 后置处理器ConfigDataEnvironmentPostProcessor创建了很多ConfigDataEnvironmentContributor,对于spring.config.import、spring.config.additional-locatio和spring.config.location配置项,会标记为INITIAL_IMPORT状态并添加ConfigDataProperties参数(ConfigDataEnvironment()->createContributors()->getInitialImportContributors()->addInitialImportContributors()->ConfigDataEnvironmentContributor.ofInitialImport()),之后使用ConfigDataLocationResolvers利用参数进行解析,然后调用加载器进行加载(ConfigDataEnvironmentContributors#withProcessedImports->ConfigDataImporter#resolveAndLoad())。
整个处理链里,大部分都是spring定义好的固定流程,需要我们自定义的是解析器和加载器,nacos实现的解析器是NacosConfigDataLocationResolver,实现的加载器NacosConfigDataLoader,两个类都是通过spring.factories文件注入的