淘先锋技术网

首页 1 2 3 4 5 6 7

记录:385

场景:在Spring Boot微服务中,读取application.yml或者bootstrap.yml等yml文件配置,转换为Map<String, Object>方式存储,使用(key,value)键值对方式,开箱即用。

1.基础说明

1.1转换原因

使用snakeyaml框架的org.yaml.snakeyaml.Yaml,从微服务的../src/main/resources资源加载yml文件,会存储在Map<String, Object> mapFromYml中。

(1)yml文件配置示例

server:
  servlet:
    context-path: /hub-example
spring:
  jackson:
    time-zone: GMT+8

(2)Yaml加载Map存储方式

Yaml加载Map存储方式:按照yml文件格式,一个冒号前面字符串就是一个Map的key,一个冒号后面部分是一个Map,直到冒号后面是具体值时,才是一个具体Object。因此,Yaml加载的结果Map<String, Object>在里面嵌套了很多层Map,最后一层Map的value才是具体值。具体格式,可以参考如下图。

使用不足:Yaml加载的Map,(key,value)键值对无法直接使用,使用时需要多次遍历才能找到最后值。因此,需转换。

(3)把Yaml加载的Map转换为可以直接使用的Map

yml配置示例:

server:
  servlet:
    context-path: /hub-example
spring:
  jackson:
    time-zone: GMT+8

转换后存储方式:

spring.jackson.time-zone=GMT+8

server.servlet.context-path=/hub-example

转换后取值方式:

Object value = Map.get(“spring.jackson.time-zone”)

1.2核心依赖

(1)功能

使用snakeyaml框架加载yml文件。

(2)依赖包

jar包:snakeyaml-1.33.jar

pom.xml依赖:

<dependency>
  <groupId>org.yaml</groupId>
  <artifactId>snakeyaml</artifactId>
  <version>1.33</version>
</dependency>

2.示例一:根据yml文件名读取yml配置和转换为Map<String, Object>

2.1实现方法

//1.从yml文件中加载
public static Map<String, Object> loadFromYmlFile(String ymlFileName) {
  InputStream input = null;
  Map<String, Object> mapFromYml = null;
  if (StringUtils.isBlank(ymlFileName)) {
      return null;
  }
  try {
      ClassPathResource resource = new ClassPathResource(ymlFileName);
      input = resource.getInputStream();
      Yaml yaml = new Yaml();
      mapFromYml = yaml.loadAs(input, Map.class);
  } catch (Exception e) {
      e.printStackTrace();
  } finally {
      if (input != null) {
          try {
              input.close();
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
  }
  return mapFromYml;
}
//2.第一层转换
public static Map<String, Object> convertYml(Map<String, Object> mapFromYml) {
  Map<String, Object> map = mapFromYml;
  Map<String, Object> mapAfterConvert = new HashMap<>();
  map.forEach((key1, value1) -> {
      if (value1 instanceof Map) {
          mapAfterConvert.putAll(forEachYml(mapAfterConvert, key1, (Map) value1));
      } else {
          mapAfterConvert.put(key1, value1.toString());
      }
  });
  mapAfterConvert.forEach((key,value)->{
      System.out.println(key+"="+value);
  });
  return mapAfterConvert;
}
// 3.第二层转换(递归转换)
public static Map<String, Object> forEachYml(Map<String, Object> mapAfterConvert, String key1, Map<String, Object> map) {
  map.forEach((key2, value2) -> {
      String strNew;
      if (StringUtils.isNotEmpty(key1)) {
          strNew = key1 + "." + key2;
      } else {
          strNew = key2;
      }
      if (value2 instanceof Map) {
          mapAfterConvert.putAll(forEachYml(mapAfterConvert, strNew, (Map) value2));
      } else {
          mapAfterConvert.put(strNew, value2);
      }
  });
  return mapAfterConvert;
}
// 4.转换为(key,value),直接使用版本
public static Map<String, Object> getYmlAllConfig(String ymlFileName) {
    return convertYml(loadFromYmlFile(ymlFileName));
}

2.2测试方法

public static void main(String[] args) {
  Map<String, Object> application = getYmlAllConfig("application.yml");
}

3.示例二:读取yml文件使用单例方式存储转换结果Map<String, Object>

3.1实现方法

public final class YmlFileUtil02 {
  private static YmlFileUtil02 ymlFileUtil = null;
  private String ymlFileName = "application.yml";
  // org.yaml.snakeyaml.Yaml从yml文件加载的Map(需逐层遍历才能定位到具体值)
  private Map<String, Object> mapFromYml = new HashMap<>();
  // 转换后的Map,直接使用getValue(key)获取值
  private Map<String, Object> mapAfterConvert = new HashMap<>();
  //从yml文件中加载与转换
  private YmlFileUtil02(String ymlFileName) {
    InputStream input = null;
    if (StringUtils.isNotBlank(ymlFileName)) {
        this.ymlFileName = ymlFileName;
    }
    try {
        ClassPathResource resource = new ClassPathResource(this.ymlFileName);
        input = resource.getInputStream();
        Yaml yaml = new Yaml();
        this.mapFromYml = yaml.loadAs(input, Map.class);
        convertYml();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (input != null) {
            try {
                input.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
  }
  // 第一层转换
  private void convertYml() {
    Map<String, Object> map = this.mapFromYml;
    map.forEach((key1, value1) -> {
        if (value1 instanceof Map) {
            this.mapAfterConvert.putAll(forEachYml(key1, (Map) value1));
        } else {
            this.mapAfterConvert.put(key1, value1.toString());
        }
    });
  }
  // 第二层转换(递归转换)
  private Map<String, Object> forEachYml(String key1, Map<String, Object> map) {
    map.forEach((key2, value2) -> {
        String strNew;
        if (StringUtils.isNotEmpty(key1)) {
            strNew = key1 + "." + key2;
        } else {
            strNew = key2;
        }
        if (value2 instanceof Map) {
            this.mapAfterConvert.putAll(this.forEachYml(strNew, (Map) value2));
        } else {
            this.mapAfterConvert.put(strNew, value2);
        }
    });
    return this.mapAfterConvert;
  }
  // 转换为(key,value),直接使用
  public Object getValue(String key) {
    return this.mapAfterConvert.get(key);
  }
      // 根据文件名创建单例对象
  public static YmlFileUtil02 getInstance(String ymlFileName) {
    if (ymlFileUtil == null) {
        Class<YmlFileUtil02> obj = YmlFileUtil02.class;
        synchronized (obj) {
            if (ymlFileUtil == null) {
                ymlFileUtil = new YmlFileUtil02(ymlFileName);
            }
        }
    }
    return ymlFileUtil;
  }
}

3.2测试方法

public static void main(String[] args) {
  YmlFileUtil02 ymlInstance = YmlFileUtil02.getInstance("application.yml");
  System.out.println("server.port=" + ymlInstance.getValue("server.port"));
  System.out.println("spring.jackson.time-zone=" + ymlInstance.getValue("spring.jackson.time-zone"));
}

4.测试yml文件

4.1文件名

application.yml

4.2文件内容

server:
  servlet:
    context-path: /hub-example
  port: 18080
spring:
  jackson:
    time-zone: GMT+8
  application:
    name: hub-example-custom
  cloud:
    nacos:
      server-addr: 127.0.0.1:8848
      username: nacos
      password: nacos
      namespace: d4f554cb-941e-40bc-85ce-f7a16c0dd4ba
    config:
      server-addr: 127.0.0.1:8848
      username: nacos
      password: nacos
      namespace: d4f554cb-941e-40bc-85ce-f7a16c0dd4ce
      group: DEFAULT_GROUP
      file-extension: yaml
      shared-configs:
        - dataId: hub01.yml
          refresh: true
          group: DEFAULT_GROUP
        - dataId: hub02.yml
          refresh: true
          group: DEFAULT_GROUP
        - dataId: hub03.yml
          refresh: true
          group: DEFAULT_GROUP

4.3注意事项

在yml文件中,以-短横线开头的配置,对应成Java对象是List类型。因此这类的(key,value)是这样的:

key=spring.cloud.config.shared-configs

value=ArrayList

以上,感谢。

2023年3月13日