SpringBoot动态配置Nacos

重要知识点

  • Nacos属性的简单使用
  • 将SpringBoot中的所有配置全部放入到Nacos中
  • 开发人创建单独的命名空间,修改互不影响
  • Nacos经常变动的配置抽离到外部文件中

将项目中的所有配置全部放到到

1. 首先引入包

java 复制代码
		<!-- nacos 接入-->
           <!-- https://mvnrepository.com/artifact/com.alibaba.boot/nacos-config-spring-boot-starter -->
           <dependency>
               <groupId>com.alibaba.boot</groupId>
               <artifactId>nacos-config-spring-boot-starter</artifactId>
               <version>最新版本号</version>
           </dependency>

2. 配置Nacos的配置文件

  • 主配置; 主要是加载 静态配置 文件的; SpringBoot在启动的时候会先去Nacos中读取配置文件然后加载到Environment中; 静态配置不启用自动刷新;
复制代码
java 复制代码
#(下面的配置中只需要吧对应的data-ids、group、data-id 、server-addr、namespace修改为对应数据)#

## -----------------静态文件配置(启动的时候最优先加载 log-enable: true)-------------- ##
nacos:
  config:
    bootstrap:
      log-enable: true # 支持日志级别的加载时机(如果需要集成dubbo,请使用此配置加载时机)
      enable: true # 设置参数nacos.config.bootstrap.enable=true,开启配置预加载功能

    max-retry: 10 # 主配置 最大重试次数
    auto-refresh: false # 主配置 是否开启自动刷新 (false:不监听属性变化,true:监听属性变化,但是对应的属性注解上也必须设置autoRefreshed = true才可以实时更新)
    config-retry-time: 3000 # 主配置 重试时间
    config-long-poll-timeout: 30000 # 主配置 配置监听长轮询超时时间
    enable-remote-sync-config: false # 主配置 开启注册监听器预加载配置服务(除非特殊业务需求,否则不推荐打开该参数)
    #允许nacos上的配置优先于本地配置; 意思是:如果nacos配置了某属性,然后在本地的application.yml配置了相同属性;那么会以nacos配置的优先;否则是本地的优先
    remote-first: true


    # 主配置 data-ids  (可以配置多个,如果多个配置文件中有相同的 属性 则优先取最前面的值)
    data-ids: application-static.properties,logback-static.xml
    group: PROJECT_EXAMPLE # 主配置 group-id
    type: yaml # 主配置 配置文件类型
    server-addr: 127.0.0.1:8848
    namespace: # 命名空间
    name: nacos
    password: nacos

上面的静态配置修改最后面的几个属性就行了;

  • 动态配置 动态配置有两种配置 一种是在yml配置文件中配置
java 复制代码
nacos:
  config:
    ext-config[0]:
      data-id:  dynamic.properties
      group: PROJECT_EXAMPLE
      #namespace:  # 命名空间 该属性如果为空 或者注释该属性  会继承主属性的命名空间
      # 该属性不继承上面的主配置    是否开启自动刷新 (false:不监听属性变化,true:监听属性变化,但是对应的属性注解上也必须设置autoRefreshed = true才可以实时更新)
      auto-refresh : true
      type: properties  # 类型 {@link com.alibaba.nacos.api.config.ConfigType}

    ## 如果还想加载其他的配置  copy一下 数组改成1 例如ext-config[1]

    ext-config[1]:
      data-id:  dynamic.yaml
      group: PROJECT_EXAMPLE
      #namespace:  # 命名空间 该属性如果为空 或者注释该属性  会继承主属性的命名空间
      # 该属性不继承上面的主配置    是否开启自动刷新 (false:不监听属性变化,true:监听属性变化,但是对应的属性注解上也必须设置autoRefreshed = true才可以实时更新)
      auto-refresh: true
      type: yaml  # 类型 {@link com.alibaba.nacos.api.config.ConfigType}

一种用注解的形式@NacosPropertySource 例如上面的动态配置跟下面效果一样

复制代码
java 复制代码
@NacosPropertySource(dataId = "dynamic.properties",groupId = "PROJECT_EXAMPLE",type = ConfigType.PROPERTIES,autoRefreshed = true)
@NacosPropertySource(dataId = "dynamic.yaml",groupId = "PROJECT_EXAMPLE",type = ConfigType.YAML,autoRefreshed = true)

两者的区别: @NacosPropertySource不可以单独指定namespace; 配置文件的方式可以单独指定namespace ; 如果他们都不指定的话,默认继承自 主配置中的namespace配置

3.Nacos的简单应用

3.1 使用@NacosConfigurationProperties将配置绑定到对象中

例子:

java 复制代码
/**
 * @author shirenchuang
 * @date 2020/10/20  9:56 上午
 *
 * 将配置中的 list 和listMap 解析并绑定到这个对象中
 * 并可以使用 @Autowired注解使用这个对象
 *
 * nacos中的配置:
 *
 * test.list:
 *     - 1
 *     - 2
 *     - 3
 *     - 4
 * test.listMap:
 *     key-1:
 *         - 1
 *         - 2
 *         - 3
 *         - 4
 *     key-2:
 *         - aa
 *         - dd
 *         - ee
 *         - rr
 *
 *
 */

@NacosConfigurationProperties( prefix = "test1",dataId = "test.yaml",groupId = "PROJECT_EXAMPLE",type = ConfigType.YAML, autoRefreshed = true)
@Configuration
public class Apple {

    private List<String> list;

    private Map<String, List<String>> listMap;

    public List<String> getList() {
        return list;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public Map<String, List<String>> getListMap() {
        return listMap;
    }

    public void setListMap(Map<String, List<String>> listMap) {
        this.listMap = listMap;
    }

    @Override
    public String toString() {
        return "Apple{" + "list=" + list + ", listMap=" + listMap + '}';
    }
}

使用的时候就直接用注解@Autowired可以直接使用

java 复制代码
@Autowired private Apple apple;
3.2 使用@NacosValue获取属性
java 复制代码
    // 是否自动刷新属性 必须在每个属性上都要标注
    @NacosValue(value = "${name:66}",autoRefreshed = true)
    private String name;

如果属性值不存在就取 冒号 后面的默认值;

3.3 也可以使用@Value来获取Nacos配置中的属性值
java 复制代码
    @NacosValue(value = "${spring.datasource.url}",autoRefreshed = true)
    private String url;

    @Value(value = "${spring.datasource.url}")
    private String url2;

例如上面的spring.datasource.url的属性是配置在nacos中的;但是上面两个注解都正常打印出来了它的值,为什么呢?@Value是从Spring容器中的Environment中获取对应的属性值,但是在启动Nacos的时候就把数据给加载到了Environment中去了; 所以通过@Value也能获取到属性值; 但是它跟@NacosValue的区别是, 它不能够实时刷新数据; 它的值一直都是启动时候第一遍加载的数据 所以我们也可以用@Value来获取我们在Nacos中配置的静态数据;作用是一样的 【SpringBoot】配置文件的加载与属性值的绑定

4.将Nacos一部分配置移动到外部文件中

我们在开发过程中,会经常涉及到配置文件的改动; 还有发布的时候不同环境用不同的配置; 这些配置写在项目的属性文件里面不太妥当; 最好是能够单独拎出来;

例如上面主配置中的 几个属性在不同环境中会经常频繁改动;那我们把他们单独拎出来存放到外部配置文件中;

Spring中读取外部配置文件的方式有很多;但是有一点很重要;就是加载时机的问题 例如@PropertySource()这个注解基本上是等容器都启动完成的时候才去解析的; 那么我们启动项目的时候是一开始就加载了Nacos的; 就必须我们在加载Nacos的配置文件之前就必须把Nacos需要的配置数据准备好并加载到 Environment 中去; 相关知识点可以参考: 【Spring Boot 四】启动之准备系统环境environmentPrepared 【SpringBoot】SpringBoot启动流程图和扩展点说明

那么最终决定我们可以选择 实现一个 EnvironmentPostProcessor扩展类;

NacosEnvPostProcessor扩展类

java 复制代码
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.logging.DeferredLog;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.DefaultPropertySourceFactory;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;
import org.springframework.util.StringUtils;

import java.io.IOException;

/**
 * @author shirenchuang
 * @date 2020/7/22  4:58 下午
 *
 * 这个优先级必须要比 NacosConfigEnvironmentProcessor 的优先级高
 * 不然的话 NacosConfigEnvironmentProcessor 执行的时候 会取不到 Nacos的这几个配置属性
 * 将文件中的属性放到 系统环境变量中; 优先级只会比StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME低
 */



import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.logging.DeferredLog;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.DefaultPropertySourceFactory;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;
import org.springframework.util.StringUtils;

import java.io.IOException;

/**
 * @author shirenchuang
 * @date 2020/7/22  4:58 下午
 * 在最初的时候将 外部配置文件属性加载到容器中
 *
 * 这个优先级必须要比 NacosConfigEnvironmentProcessor 的优先级高
 * 不然的话 NacosConfigEnvironmentProcessor 执行的时候 会取不到 Nacos的这几个配置属性
 * 将文件中的属性放到 系统环境变量中; 优先级只会比StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME低
 */


public class ExternalFilePostProcessor implements EnvironmentPostProcessor, ApplicationListener<ApplicationEvent>, Ordered {


    /**
     * 这个时候Log系统还没有初始化  使用DeferredLog来记录  并在onApplicationEvent进行回放
     * */
    private static final DeferredLog LOGGER = new DeferredLog();

    private  ResourceLoader resourceLoader = new DefaultResourceLoader();




    @Override
    public int getOrder() {
        return 0;
    }


    /**
     * 将文件中的nacos配置文件 加载到 environment中;
     * 方便Nacos可以拿到 静态配置
     *
     * @param environment
     * @param application
     */

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        String filePath = environment.getProperty("nacos_file_path");
        if(StringUtils.isEmpty(filePath)){
            LOGGER.info("\n [----未配置外部属性路径-----] \n");
            return;
        }
        String resolvedLocation = environment.resolveRequiredPlaceholders(filePath);
        resolvedLocation = "file:"+resolvedLocation;
        PropertySourceFactory factory = new DefaultPropertySourceFactory();
        Resource resource = this.resourceLoader.getResource(resolvedLocation);
        try {
            addPropertySource(factory.createPropertySource(filePath, new EncodedResource(resource)),environment);
        } catch (IOException e) {
            LOGGER.error(" 外部配置路径 - Properties location [" + filePath + "] not resolvable: " + e.getMessage());
        }


    }

    private void addPropertySource(PropertySource<?> propertySource,ConfigurableEnvironment environment) {
        MutablePropertySources propertySources = environment.getPropertySources();
        //添加到 systemEnvironment  优先级只比 系统属性 和命令行属性等等的低
        propertySources.addAfter(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, propertySource);
    }


    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        LOGGER.replayTo(ExternalFilePostProcessor.class);
    }
}

然后配置一下spring.factories;

java 复制代码
org.springframework.boot.env.EnvironmentPostProcessor=com.daimler.example.service.common.NacosEnvPostProcessor

相关知识点 【SpringBoot 二】spring.factories加载时机分析

然后本地创建一个文件

在yml中配置文件路径

java 复制代码
#nacos 的基本配置(放在服务器本地文件上, 包含  nacos.config.server-addr、 nacos.config.namespace、 username、password)
nacos_file_path: /**/**/nacos_static/project_example-nacos.properties
Spring中实现上面的功能没有那么复杂
java 复制代码
@EnableNacosConfig(globalProperties = @NacosProperties(username = "${nacos.config.username}", password = "${nacos.config.password}",
    serverAddr = "${nacos.config.server-addr}",namespace = "${nacos.config.namespace}"   ))
@NacosPropertySource(dataId = "dynamic.yaml",groupId = "PROJECT_EXAMPLE",type = ConfigType.YAML,autoRefreshed = true)
public class NacosConfig {

}


@Configuration
@PropertySource(value = "file:/etc/nacos.properties")
@ComponentScan(basePackages = "com.xx")
@Import( value = {NacosConfig.class})
public class SpringConfig {

}

然后外部配置文件配置

5.每个开发人员一套自己的配置

在本地开发过程中,我们可能会经常修改配置文件;但可能改了之后会影响到别人;就有这样的需求;我改配置的时候 只影响我自己;

我们可以看到上面的配置中 有一个配置 nacos.config.namespace= ; 这个是命名空间;通过这样一个属性我们可以应用不同的配置;

具体用法

  • 新建一个属于自己的命名空间
  • 将需要克隆的配置 从指定空间克隆到自己刚刚创建的空间
  • 将自己本地nacos属性配置的namespace 改成自己刚刚创建的命名空间id(注意是 ID)
  • 重新启动,搞定!

完整的yml配置

java 复制代码
#nacos 的基本配置(放在服务器本地文件上, 包含  nacos.config.server-addr、 nacos.config.namespace、 username、password)
nacos_file_path: /**/**/nacos_static/project_example-nacos.properties



#(下面的配置中只需要吧对应的data-ids、group、data-id 修改为对应数据)#

## -----------------静态文件配置(启动的时候最优先加载 log-enable: true)-------------- ##
nacos:
  config:
    bootstrap:
      log-enable: true # 支持日志级别的加载时机(如果需要集成dubbo,请使用此配置加载时机)
      enable: true # 设置参数nacos.config.bootstrap.enable=true,开启配置预加载功能

    max-retry: 10 # 主配置 最大重试次数
    auto-refresh: false # 主配置 是否开启自动刷新 (false:不监听属性变化,true:监听属性变化,但是对应的属性注解上也必须设置autoRefreshed = true才可以实时更新)
    config-retry-time: 3000 # 主配置 重试时间
    config-long-poll-timeout: 30000 # 主配置 配置监听长轮询超时时间
    enable-remote-sync-config: false # 主配置 开启注册监听器预加载配置服务(除非特殊业务需求,否则不推荐打开该参数)
    #允许nacos上的配置优先于本地配置; 意思是:如果nacos配置了某属性,然后在本地的application.yml配置了相同属性;那么会以nacos配置的优先;否则是本地的优先
    remote-first: true


    # 主配置 data-ids  (可以配置多个,如果多个配置文件中有相同的 属性 则优先取最前面的值)
    data-ids: 静态配置1,静态配置2,logback.xml
    group: 自己的group # 主配置 group-id
    type: yaml # 主配置 配置文件类型


    ## ---------------如果 使用配置方式就是下面的; 如果用@NacosPropertySource注解方式就删掉下面的 ---------------------------  ##
    ext-config[0]:
      data-id:  自己的data-id
      group: 自己的group
      #namespace:  # 命名空间 该属性如果为空 或者注释该属性  会继承主属性的命名空间
      # 该属性不继承上面的主配置    是否开启自动刷新 (false:不监听属性变化,true:监听属性变化,但是对应的属性注解上也必须设置autoRefreshed = true才可以实时更新)
      auto-refresh : true
      type: properties  # 类型 {@link com.alibaba.nacos.api.config.ConfigType}

    ## 如果还想加载其他的配置  copy一下 数组改成1 例如ext-config[1]

    ext-config[1]:
      data-id:  自己的data-id
      group: 自己的group
      #namespace:  # 命名空间 该属性如果为空 或者注释该属性  会继承主属性的命名空间
      # 该属性不继承上面的主配置    是否开启自动刷新 (false:不监听属性变化,true:监听属性变化,但是对应的属性注解上也必须设置autoRefreshed = true才可以实时更新)
      auto-refresh: true
      type: yaml  # 类型 {@link com.alibaba.nacos.api.config.ConfigType}

外部配置文件 /**/**/nacos_static/project_example-nacos.properties

java 复制代码
# 主配置服务器地址
   nacos.config.server-addr=127.0.0.1:8848
   nacos.config.username=nacos
   nacos.config.password=nacos
   # 命名空间 (空为默认的public)
   nacos.config.namespace=
相关推荐
無限進步D3 小时前
Java 运行原理
java·开发语言·入门
難釋懷3 小时前
安装Canal
java
是苏浙3 小时前
JDK17新增特性
java·开发语言
不光头强3 小时前
spring cloud知识总结
后端·spring·spring cloud
GetcharZp6 小时前
告别 Python 依赖!用 LangChainGo 打造高性能大模型应用,Go 程序员必看!
后端
阿里加多7 小时前
第 4 章:Go 线程模型——GMP 深度解析
java·开发语言·后端·golang
likerhood7 小时前
java中`==`和`.equals()`区别
java·开发语言·python
小小李程序员7 小时前
Langchain4j工具调用获取不到ThreadLocal
java·后端·ai