springboot集成nacos读取nacos配置数据的原理

您所在的位置:网站首页 springboot读取application方法 springboot集成nacos读取nacos配置数据的原理

springboot集成nacos读取nacos配置数据的原理

2023-05-29 17:49| 来源: 网络整理| 查看: 265

先前我们说了springboot应用集成nacos配置中心的过程,只需要引入一个jar包并在业务系统的配置文件中添加相关的nacos配置项,我们的系统就能获取到nacos服务上面维护的配置数据,那这个获取配置数据的原理是啥呢?接下来咱们就来聊聊这一块~

1、Nacos config springboot starter包

我们在springboot应用中集成nacos配置中心时,添加了以下依赖:

                     com.alibaba.boot             nacos-config-spring-boot-starter             0.2.11         

它会自动导入nacos-config-spring-boot-autoconfigure包和其他nacos客户端jar包。

看到nacos-config-spring-boot-autoconfigure这种自动配置包,我们要先打开这个jar包,看下包目录下的/META-INF/spring.factories文件,里面有如下内容:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\   com.alibaba.boot.nacos.config.autoconfigure.NacosConfigAutoConfiguration #org.springframework.context.ApplicationContextInitializer=\ #  com.alibaba.boot.nacos.config.autoconfigure.NacosConfigApplicationContextInitializer org.springframework.boot.env.EnvironmentPostProcessor=\   com.alibaba.boot.nacos.config.autoconfigure.NacosConfigEnvironmentProcessor org.springframework.context.ApplicationListener=\   com.alibaba.boot.nacos.config.logging.NacosLoggingListener

本次我们关注的重点是org.springframework.boot.env.EnvironmentPostProcessor这个配置项的值,它只有一个值:

com.alibaba.boot.nacos.config.autoconfigure.NacosConfigEnvironmentProcessor

我们来看下NacosConfigEnvironmentProcessor这个类,它有如下定义:

public class NacosConfigEnvironmentProcessor         implements EnvironmentPostProcessor, Ordered {             。。。。省略部分代码                     @Override     public void postProcessEnvironment(ConfigurableEnvironment environment,             SpringApplication application) {         application.addInitializers(new NacosConfigApplicationContextInitializer(this));         nacosConfigProperties = NacosConfigPropertiesUtils                 .buildNacosConfigProperties(environment);         if (enable()) {             System.out.println(                     "[Nacos Config Boot] : The preload log configuration is enabled");             loadConfig(environment);             NacosConfigLoader nacosConfigLoader = NacosConfigLoaderFactory.getSingleton(nacosConfigProperties, environment, builder);             LogAutoFreshProcess.build(environment, nacosConfigProperties, nacosConfigLoader, builder).process();         }     }          }

NacosConfigEnvironmentProcessor就做了一件事,往spring容器中添加了NacosConfigApplicationContextInitializer初始化器,后续由它完成从nacos配置中心加载数据的操作。

1.1、重要的NacosConfigEnvironmentProcessor是在哪执行的

这个问题等我们看完下面的代码就有了答案了~

1.2、重要的NacosConfigApplicationContextInitializer是在哪执行的

这个问题等我们看完下面的代码也会有答案了~

2、应用的启动过程

我们从以下的启动类入手。

@SpringBootApplication public class App {     public static void main(String[] args) {         SpringApplication.run(App.class, args);     } } 2.1、SpringApplication的构造方法做了啥

跟踪上面SpringApplication的run方法,SpringApplication.run方法内部会先创建一个SpringApplication对象,然后再调用该对象的另一个run实例方法。我们先进入SpringApplication的构造方法:

public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {         this.resourceLoader = resourceLoader;         Assert.notNull(primarySources, "PrimarySources must not be null");         this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));         this.webApplicationType = WebApplicationType.deduceFromClasspath();         this.bootstrapRegistryInitializers = new ArrayList(                 getSpringFactoriesInstances(BootstrapRegistryInitializer.class));         //从类路径下面的META-INF/spring.factories文件中根据ApplicationContextInitializer配置值来初始化ApplicationContextInitializer实例         setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));     //从类路径下面的META-INF/spring.factories文件中根据ApplicationListener配置值来初始化ApplicationListener实例         setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));         this.mainApplicationClass = deduceMainApplicationClass();     }

其中getSpringFactoriesInstances方法用于从类路径下面的META-INF/spring.factories文件中获取指定配置项对应的类的全路径名列表(多个类的全路径名之间用英文逗号隔开),根据类的全路径名创建相应的对象。

2.1.1、构造方法中的setInitializers方法

setInitializers方法用于设置容器的初始化器对象集合,它的参数来源于getSpringFactoriesInstances(ApplicationContextInitializer.class)的执行结果。getSpringFactoriesInstances方法从类路径下面的META-INF/spring.factories文件中获取ApplicationContextInitializer对应的类的全路径名列表,根据类的全路径名创建相应的对象。

2.1.2、构造方法中的setListeners方法

setListeners方法用于设置容器的ApplicationListener监听器对象,它的参数来源于getSpringFactoriesInstances(ApplicationListener.class)的执行结果。getSpringFactoriesInstances方法从类路径下面的META-INF/spring.factories文件中获取ApplicationListener对应的类的全路径名列表,根据类的全路径名创建相应的对象。spring的监听器就是监听器模式的一种实现,它可以设置自己感兴趣的事件,并且在相应事情发生时能接到通知并对该事件进行处理。我们在spring-boot.jar包下的META-INF/spring.factories可以看到如下的内容:

# Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.ClearCachesApplicationListener,\ org.springframework.boot.builder.ParentContextCloserApplicationListener,\ org.springframework.boot.context.FileEncodingApplicationListener,\ org.springframework.boot.context.config.AnsiOutputApplicationListener,\ org.springframework.boot.context.config.DelegatingApplicationListener,\ org.springframework.boot.context.logging.LoggingApplicationListener,\ org.springframework.boot.env.EnvironmentPostProcessorApplicationListener

这些监听器类都会被实例化,并存入spring容器的ApplicationListener监听器列表中。

2.1.3、重要的EnvironmentPostProcessorApplicationListener出现了

经过了2.1.2部分的代码观摩,我们知道容器的ApplicationListener监听器列表中已经有了EnvironmentPostProcessorApplicationListener对象,EnvironmentPostProcessorApplicationListener很重要,它能监听到ApplicationEnvironmentPreparedEvent类型的事件,并且会触发EnvironmentPostProcessor实例的执行。我们在第1部分讲到的NacosConfigEnvironmentProcessor就是个EnvironmentPostProcessor的实现类。此处我们先打个标记,后面我们会说下1.1小节的NacosConfigEnvironmentProcessor实例对象是在哪执行的。

2.2、SpringAppLication的run方法执行流程

完成2.1节的构造方法后,会执行如下的run方法(本方法很重要):

    public ConfigurableApplicationContext run(String... args) {         long startTime = System.nanoTime();         DefaultBootstrapContext bootstrapContext = createBootstrapContext();         ConfigurableApplicationContext context = null;         configureHeadlessProperty();         SpringApplicationRunListeners listeners = getRunListeners(args);         listeners.starting(bootstrapContext, this.mainApplicationClass);         try {             ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);             //准备环境信息(读取应用系统需要的所有配置项)             ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);             configureIgnoreBeanInfo(environment);             Banner printedBanner = printBanner(environment);             context = createApplicationContext();             context.setApplicationStartup(this.applicationStartup);             //准备容器上下文             prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);             //刷新容器上下文(内部就是执行出名的refresh方法)             refreshContext(context);             。。。省略部分代码             callRunners(context, applicationArguments);         }         。。省略部分代码                  return context;     }

结合我们本篇要说读取nacos配置中心的原理,本次我们主要关注getRunListeners、prepareEnvironment方法和prepareContext这三个方法,我们先按照代码的执行顺序来依次看下这几个方法。

2.2.1、getRunListeners方法

getRunListeners方法如下:

private SpringApplicationRunListeners getRunListeners(String[] args) {    Class[] types = new Class[] { SpringApplication.class, String[].class };    return new SpringApplicationRunListeners(logger,          getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),          this.applicationStartup); }

它会读取类路径下面的META-INF/spring.factories文件中SpringApplicationRunListener配置项对应的值,并创建相应的实例对象,赋值给SpringApplicationRunListeners的listeners属性(List类型),并返回SpringApplicationRunListeners对象。由于spring-boot包的META-INF/spring.factories文件有如下内容:

# Run Listeners org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener

所以SpringApplicationRunListeners的listeners属性中就包含了EventPublishingRunListener实例对象,而且实际上只有这一个对象。

2.2.2、prepareEnvironment方法开始执行

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);         //通过监听器通知环境信息已准备好,触发EnvironmentPostProcessor的执行         listeners.environmentPrepared(bootstrapContext, environment);         DefaultPropertiesPropertySource.moveToEnd(environment);         Assert.state(!environment.containsProperty("spring.main.environment-prefix"),                 "Environment prefix cannot be set via properties.");         bindToSpringApplication(environment);         if (!this.isCustomEnvironment) {             EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader());             environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());         }         ConfigurationPropertySources.attach(environment);         return environment;     }

此处划重点,在执行listeners.environmentPrepared方法之前,我们的操作系统环境变量、jvm系统属性和业务系统的application.properties(以及其他的配置文件)的配置项信息都被填充到了容器的environment对象中。

2.2.1部分的getRunListeners方法返回的SpringApplicationRunListeners对象会作为参数传递给prepareEnvironment方法的listeners参数,而listeners.environmentPrepared就是执行SpringApplicationRunListeners的environmentPrepared方法,方法内容如下:

//SpringApplicationRunListeners类 void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {         doWithListeners("spring.boot.application.environment-prepared",                 (listener) -> listener.environmentPrepared(bootstrapContext, environment));     }

里面的doWithListeners方法最后会执行到重载的doWithListeners方法:

//SpringApplicationRunListeners类 private void doWithListeners(String stepName, Consumer listenerAction,             Consumer stepAction) {         StartupStep step = this.applicationStartup.start(stepName);         this.listeners.forEach(listenerAction);         if (stepAction != null) {             stepAction.accept(step);         }         step.end();     }

前面2.2.1部分我们说了,listeners对象就是个list,此处它只有一个元素,是EventPublishingRunListener类型的实例对象。

listenerAction对象接收的是前面environmentPrepared方法内部传递的lambda表达式:

(listener) -> listener.environmentPrepared(bootstrapContext, environment)

当执行this.listeners.forEach(listenerAction)时,就会进入EventPublishingRunListener类的environmentPrepared方法:

//EventPublishingRunListener类 public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,             ConfigurableEnvironment environment) {         this.initialMulticaster.multicastEvent(                 new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));     }

此处会调用SimpleApplicationEventMulticaster类的multicastEvent方法,并传递ApplicationEnvironmentPreparedEvent事件进行广播。

//另外此处要先说明下:当spring创建EventPublishingRunListener对象的时候,就已经将SpringApplication对象的ApplicationListener对象列表添加到了EventPublishingRunListener对象的initialMulticaster属性的监听器列表中,EventPublishingRunListener构造函数代码如下: public EventPublishingRunListener(SpringApplication application, String[] args) {         this.application = application;         this.args = args;         this.initialMulticaster = new SimpleApplicationEventMulticaster();         for (ApplicationListener listener : application.getListeners()) {             this.initialMulticaster.addApplicationListener(listener);         }     }

然后我们继续进入SimpleApplicationEventMulticaster类的multicastEvent方法:

//SimpleApplicationEventMulticaster类 public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {         ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));         Executor executor = getTaskExecutor();         for (ApplicationListener listener : getApplicationListeners(event, type)) {             if (executor != null) {                 executor.execute(() -> invokeListener(listener, event));             }             else {                 //会直接调用监听器                 invokeListener(listener, event);             }         }     }

这里的getApplicationListeners(event, type)会根据事件类型获取对该事件感兴趣的监听器列表,然后挨个调用监听器对象,也就是触发各个监听器的处理流程。由于此处的event参数是接收了ApplicationEnvironmentPreparedEvent对象,那么getApplicationListeners方法就会获取到EnvironmentPostProcessorApplicationListener对象(2.1.3部分咱们提到过它)。为啥呢?我们进入EnvironmentPostProcessorApplicationListener类,它有如下的supportsEventType方法:

//EnvironmentPostProcessorApplicationListener类 @Override     public boolean supportsEventType(Class


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3