IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Spring Cloud Alibaba Nacos统一配置源码分析-nacos配置初始化加载 -> 正文阅读

[Java知识库]Spring Cloud Alibaba Nacos统一配置源码分析-nacos配置初始化加载

概述

搭建SpringCloudAlibabaNacos环境

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.study.nacos</groupId>
    <artifactId>nacosStudy</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>nacosStudy</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            <version>2.1.2.RELEASE</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.1.3.RELEASE</version>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

配置Nacos相关属性

spring.application.name=nacos-config-test
spring.cloud.nacos.config.server-addr=192.168.10.18:8858

启动类中添加测试代码

package com.study.nacos.nacosstudy;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class NacosStudyApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(NacosStudyApplication.class, args);
        // 从Environment获取配置
        String test = context.getEnvironment().getProperty("test");
        System.out.println(test);
    }
}

通过context.getEnvironment().getProperty(“test”);获取nacos管理的配置项。

nacos统一配置的作用

集中管理配置的CRUD

配置的动态更新

springcloud实现nacos配置初始化加载

启动类方法入口

@SpringBootApplication
public class NacosStudyApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(NacosStudyApplication.class, args);
    }
}

SpringApplication.java的run方法

public static ConfigurableApplicationContext run(Class<?>[] primarySources,
			String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

我们继续跟踪new SpringApplication(primarySources).run(args);的run方法。
进入run方法,我们就看到的核心代码,真正创建应用的上下文的地方.
这里,我会只摘取对nacos配置加载和动态更新有用的入口代码。

public ConfigurableApplicationContext run(String... args) {
		ConfigurableApplicationContext context = null;
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			// 这里预处理环境(就是找到所有配置文件的位置)
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			// 预处理上下文,加载所有配置信息到enviroment中,供创建bean使用。
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			refreshContext(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}
		return context;
	}

nacos配置位置查找

ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
//
private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		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;
	}
  • 接下来发送ApplicationEnvironmentPreparedEvent事件
public void environmentPrepared(ConfigurableEnvironment environment) {
		this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
				this.application, this.args, environment));
	}
// 中间省略中间的跟踪代码,注意走executor为空的代码分支。
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
		try {
			listener.onApplicationEvent(event);
		}
		...
	}
  • 找到事件接收者
    我们在越多spring源码时会经常迷失,迷失的原因是spring中使用了大量的观察者模式,往往都是先根据被观察者的类型收集大量的观察观察者(比如Listener),然后循环执行观察者方法。
    这里介绍一个技巧,找实现了ApplicationEnvironmentPreparedEvent事件的接口(观察者),因为预处理环境就发送的这个事件。
    在这里插入图片描述
  • 这样我们就来到了BootstrapApplicationListener.java的onApplicationEvent方法中
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        ConfigurableEnvironment environment = event.getEnvironment();
        if ((Boolean)environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class, true)) {
            if (!environment.getPropertySources().contains("bootstrap")) {
                ConfigurableApplicationContext context = null;
                ....
                if (context == null) {
                	// 设置应用上下文
                    context = this.bootstrapServiceContext(environment, event.getSpringApplication(), configName);
                    event.getSpringApplication().addListeners(new ApplicationListener[]{new BootstrapApplicationListener.CloseContextOnFailureApplicationListener(context)});
                }
                this.apply(context, event.getSpringApplication(), environment);
            }
        }
    }
// 设置应用上下文
private ConfigurableApplicationContext bootstrapServiceContext(ConfigurableEnvironment environment, final SpringApplication application, String configName) {
        StandardEnvironment bootstrapEnvironment = new StandardEnvironment();
        MutablePropertySources bootstrapProperties = bootstrapEnvironment.getPropertySources();
        ...
        // 自动装配对象
        builder.sources(new Class[]{BootstrapImportSelectorConfiguration.class});
        ConfigurableApplicationContext context = builder.run(new String[0]);
        context.setId("bootstrap");
        this.addAncestorInitializer(application, context);
        bootstrapProperties.remove("bootstrap");
        this.mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties);
        return context;
    }

自动装配nacos配置类型

@Import({BootstrapImportSelector.class})
public class BootstrapImportSelectorConfiguration {
    public BootstrapImportSelectorConfiguration() {
    }
}
 public String[] selectImports(AnnotationMetadata annotationMetadata) {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        // 从META-INF/spring.factories中加载BootstrapConfiguration.class类
        List<String> names = new ArrayList(SpringFactoriesLoader.loadFactoryNames(BootstrapConfiguration.class, classLoader));
        ..
        return classNames;
    }
  • 可以找到两处加载org.springframework.cloud.bootstrap.BootstrapConfiguration的地方
    spring-cloud-starter-alibaba-nacos-config-2.1.2.RELEASE.jar的META-INF/spring.factories中
    com.alibaba.cloud.nacos.NacosConfigBootstrapConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.alibaba.cloud.nacos.NacosConfigBootstrapConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.cloud.nacos.NacosConfigAutoConfiguration,\
com.alibaba.cloud.nacos.endpoint.NacosConfigEndpointAutoConfiguration
org.springframework.boot.diagnostics.FailureAnalyzer=\
com.alibaba.cloud.nacos.diagnostics.analyzer.NacosConnectionFailureAnalyzer

spring-cloud-context-3.0.3.jar的META-INF/spring.factories中
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration

# AutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
org.springframework.cloud.autoconfigure.LifecycleMvcEndpointAutoConfiguration,\
org.springframework.cloud.autoconfigure.RefreshAutoConfiguration,\
org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration,\
org.springframework.cloud.autoconfigure.WritableEnvironmentEndpointAutoConfiguration
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.cloud.bootstrap.BootstrapApplicationListener,\
org.springframework.cloud.bootstrap.LoggingSystemShutdownListener,\
org.springframework.cloud.context.restart.RestartListener
# Spring Cloud Bootstrap components
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration,\
org.springframework.cloud.bootstrap.encrypt.EncryptionBootstrapConfiguration,\
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration
# Spring Boot BootstrapRegistryInitializer
org.springframework.boot.BootstrapRegistryInitializer=\
org.springframework.cloud.bootstrap.RefreshBootstrapRegistryInitializer
# Spring Boot Bootstrappers
org.springframework.boot.Bootstrapper=\
org.springframework.cloud.bootstrap.TextEncryptorConfigBootstrapper
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.bootstrap.encrypt.DecryptEnvironmentPostProcessor,\
org.springframework.cloud.util.random.CachedRandomPropertySourceEnvironmentPostProcessor

加载nacos客户端的配置信息

@Configuration
@ConditionalOnProperty(
    name = {"spring.cloud.nacos.config.enabled"},
    matchIfMissing = true
)
public class NacosConfigBootstrapConfiguration {
    public NacosConfigBootstrapConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean
    public NacosConfigProperties nacosConfigProperties() {
        return new NacosConfigProperties();
    }

    @Bean
    @ConditionalOnMissingBean
    public NacosConfigManager nacosConfigManager(NacosConfigProperties nacosConfigProperties) {
        return new NacosConfigManager(nacosConfigProperties);
    }

    @Bean
    public NacosPropertySourceLocator nacosPropertySourceLocator(NacosConfigManager nacosConfigManager) {
        return new NacosPropertySourceLocator(nacosConfigManager);
    }
}

可以看到,自动装配NacosConfigBootstrapConfiguration 对象后,紧接着就是生成NacosConfigProperties对象,最后初始化nacos的配置信息。

public NacosConfigProperties() {
    }

    @PostConstruct
    public void init() {
        this.overrideFromEnv();
    }

    private void overrideFromEnv() {
        if (StringUtils.isEmpty(this.getServerAddr())) {
            String serverAddr = this.environment.resolvePlaceholders("${spring.cloud.nacos.config.server-addr:}");
            if (StringUtils.isEmpty(serverAddr)) {
                serverAddr = this.environment.resolvePlaceholders("${spring.cloud.nacos.server-addr:localhost:8848}");
            }

            this.setServerAddr(serverAddr);
        }

        if (StringUtils.isEmpty(this.getUsername())) {
            this.setUsername(this.environment.resolvePlaceholders("${spring.cloud.nacos.username:}"));
        }

        if (StringUtils.isEmpty(this.getPassword())) {
            this.setPassword(this.environment.resolvePlaceholders("${spring.cloud.nacos.password:}"));
        }
    }

上下文加载nacos中的配置信息

回到SpringApplication.java的run方法

prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);

我们关注的是上下文的初始化方法applyInitializers(context)方法

private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		context.setEnvironment(environment);
		postProcessApplicationContext(context);
		// 上下文初始化
		applyInitializers(context);
		...
	}
protected void applyInitializers(ConfigurableApplicationContext context) {
		for (ApplicationContextInitializer initializer : getInitializers()) {
			Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
					initializer.getClass(), ApplicationContextInitializer.class);
			Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
			initializer.initialize(context);
		}
	}

我们找到之前spring-cloud-context-3.0.3.jar的META-INF/spring.factories中
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration
对应的PropertySourceBootstrapConfiguration类。
在Initialize方法中

 PropertySourceLocator locator = (PropertySourceLocator)var5.next();
                    source = locator.locateCollection(environment);

我们找到PropertySourceLocator的实现类NacosPropertySourceLocator,
我们查看locate方法

public PropertySource<?> locate(Environment env) {
        this.nacosConfigProperties.setEnvironment(env);
       // 创建configService服务
        ConfigService configService = this.nacosConfigManager.getConfigService();
        if (null == configService) {
            log.warn("no instance of config service found, can't load config from nacos");
            return null;
        } else {
            long timeout = (long)this.nacosConfigProperties.getTimeout();
            this.nacosPropertySourceBuilder = new NacosPropertySourceBuilder(configService, timeout);
            String name = this.nacosConfigProperties.getName();
            String dataIdPrefix = this.nacosConfigProperties.getPrefix();
            if (StringUtils.isEmpty(dataIdPrefix)) {
                dataIdPrefix = name;
            }

            if (StringUtils.isEmpty(dataIdPrefix)) {
                dataIdPrefix = env.getProperty("spring.application.name");
            }

            CompositePropertySource composite = new CompositePropertySource("NACOS");
            this.loadSharedConfiguration(composite);
            this.loadExtConfiguration(composite);
            // 加载nacos配置
            this.loadApplicationConfiguration(composite, dataIdPrefix, this.nacosConfigProperties, env);
            return composite;
        }
    }
  • 加载nacos配置
private void loadApplicationConfiguration(CompositePropertySource compositePropertySource, String dataIdPrefix, NacosConfigProperties properties, Environment environment) {
        String fileExtension = properties.getFileExtension();
        String nacosGroup = properties.getGroup();
        this.loadNacosDataIfPresent(compositePropertySource, dataIdPrefix, nacosGroup, fileExtension, true);
        ...
    }
private NacosPropertySource loadNacosPropertySource(final String dataId, final String group, String fileExtension, boolean isRefreshable) {
        return NacosContextRefresher.getRefreshCount() != 0L && !isRefreshable ? NacosPropertySourceRepository.getNacosPropertySource(dataId, group) : this.nacosPropertySourceBuilder.build(dataId, group, fileExtension, isRefreshable);
    }
NacosPropertySource build(String dataId, String group, String fileExtension, boolean isRefreshable) {
        Map<String, Object> p = this.loadNacosData(dataId, group, fileExtension);
        NacosPropertySource nacosPropertySource = new NacosPropertySource(group, dataId, p, new Date(), isRefreshable);
        NacosPropertySourceRepository.collectNacosPropertySource(nacosPropertySource);
        return nacosPropertySource;
    }
private Map<String, Object> loadNacosData(String dataId, String group, String fileExtension) {
        String data = null;

        try {
        	// 调用nacos接口查询nacos服务端中的配置信息
            data = this.configService.getConfig(dataId, group, this.timeout);
            ...
    }

spring cloud实现nacos配置的动态更新

应用上下文加载成功后会发送一个ready事件,nacosRefresh实现了该事件作为观察者;
执行事件逻辑中,把对nacos配置感兴趣的listener注册到服务器端。
客户端通过长连接+pull的方式获取服务器端配置变化,一旦配置发送变化,会直接通知到客户端,客户端根据注册的listener发送refreshEvent事件。
RefreshEventListener监听到refreshEvent事件会把更新的配置拷贝到上下文的environment中。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-05-09 12:26:14  更:2022-05-09 12:26:48 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 23:34:54-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码