【黑科技】java项目中任意处(包括static类)使用yml配置文件的值,比@Value更强大

         在spring boot项目中,支持两种配置文件的方式。一种为properties,一种为yml。使用yml格式的配置文件,结构更加清晰。

         在项目中获取配置文件的值,可以在Bean类中使用@Value注解获取配置项的值。但是如果我们需要在非Bean类(如工具类)静态方法中想使用yml中的配置项时,怎么办呢?下面我们来介绍我们系统级的解决方案:YamlUtils。

         YamlUtils功能介绍:可在项目中,任意地方获取yml配置文件中的配置项。支持上下级yml配置项覆盖,支持profile切换,支持变量值嵌套等。已提供以下多种方法:

方法名参数返回类型说明
get
keyObject获取配置项对应的值
getString
keyString获取配置项对应的字符串值
getInteger
keyInteger获取配置项对应的整形值
getLong
keyLong获取配置项对应的长整形值
getBoolean
keyBoolean获取配置项对应的布尔值
getFloat
keyFloat获取配置项对应的浮点值
getDouble
keyDouble获取配置项对应的浮点值
getMap
keyMap获取配置项对应的map值
getValueInMap
mapKey,
keyInMap
Object
获取配置项对应的map内key对应的值
getList
key
List<Object>
获取配置项对应的list值
getArray
key
String[]
获取配置项对应的array值

 

  • 功能展示:
  1. 支持profile

   2.​​​​​​ 支持复杂类型

 

  •          微服务项目结构框图

从结构上可以将我们的框架分为四个层级。

  1. 业务层;
  2. 业务依赖层,为业务公共部分。如项目中多个微服务药连接公共的IP地址、eureka地址的维护等。
  3. 公共依赖层;业务底层公共部分,不同项目也能共用的部分。做认证、鉴权、工具包集合、配置项提取等。
  4. 基础层。第三方提供的各种基础包。

      除基础层,每一层都有对应的配置文件,且配置项随结构定位也有层级依赖,一共3层,上层可覆盖下层配置项。如common-application.yml -> center-common-application.yml -> salary-application.yml。配置项如果存在于common-application.yml,如果不满足需求,需要在center-common-application.yml或者salary-application.yml进行覆盖。

  • 实现原理
  1. 在启动类首行使用SpringUtils.initSpringConfigLocationProperty()初始化需要加载的配置文件;
  2. YamlUtils类静态代码块中按照一定顺序加载初始化的所有配置文件,将所有配置项加载到内存中,同名配置项会被替换;
  3. 根据不同的方法、不同的key值获取对应的结果即可。
  • 相关代码剖析
SpringUtils
@Slf4j
public class SpringUtils {
    /**
     * 配置文件列表
     */
    @Getter
    private static List<String> configurationFileList;

    /**
     * 设置app.name配置
     *
     * @return
     */
    public static void setAPPNameProperty(String appName) {
        System.setProperty("app.name", appName);
        System.setProperty("spring.application.name", appName);
    }

    /**
     * 初始化基础配置文件路径
     *
     * @return
     */
    public static void initSpringConfigLocationProperty() {
        initSpringConfigLocationProperty(null);
    }

    /**
     * 初始化基础配置文件路径
     *
     * @param configFileLocations 配置文件位置集合
     * @return
     */
    public static void initSpringConfigLocationProperty(String... configFileLocations) {
        List<String> configOriginList = new ArrayList<>();
        configOriginList.addAll(Arrays.asList(Constants.SPRING_CONFIG_LOCATION_PROPERTY_BOOT_BASE));

        if (configFileLocations != null) {
            configOriginList.addAll(Arrays.asList(configFileLocations));
        }
        configOriginList.addAll(Arrays.asList(Constants.SPRING_CONFIG_LOCATION_PROPERTY));

        //再次调整顺序,将所有的config文件夹提前
        List<String> configurationList = configOriginList.stream().filter(
                configuration -> StringUtils.isNotEmpty(configuration)).collect(Collectors.toList());
        log.debug("configurationList: [{}]", configurationList);

        List<String> classpathConfigurationList = configurationList.stream()
                .filter(configuration -> configuration.startsWith("classpath")).collect(Collectors.toList());
        List<String> localConfigurationList = configurationList.stream()
                .filter(configuration -> configuration.startsWith("file:")).collect(Collectors.toList());

        ArrayList<String> resultList = new ArrayList<>();
        resultList.addAll(classpathConfigurationList);
        resultList.addAll(localConfigurationList);
        configurationFileList = resultList;
        log.trace("configurationFileList: [{}]", configurationFileList);

        String newConfigurationsString = resultList.stream().collect(Collectors.joining(","));
        log.info("spring.config.location: [{}]", newConfigurationsString);
        System.setProperty("spring.config.location", newConfigurationsString);
    }


}
Constants
/**
     * spring boot基础配置文件,application.properties添加在此会影响应用从外部加载配置文件
     */
    public static final String[] SPRING_CONFIG_LOCATION_PROPERTY_BOOT_BASE = new String[]{"classpath:/config/application-common.yml"};
    /**
     * spring 基础配置文件,application.properties添加在此会影响应用从外部加载配置文件
     */
    public static final String[] SPRING_CONFIG_LOCATION_PROPERTY = new String[]{"classpath:/config/application.yml", "file:config/application.yml"};
YamlUtils 
/**
 * yml配置工具类
 */
@Slf4j
public class YamlUtils {

    /**
     * 配置表
     */
    private static Map<String, Object> sourceMap = new HashMap<>();

    private static final String HEAD = "${";
    private static final String TAIL = "}";

    static {
        loadAll();
        checkRootParentPath();
    }

    /**
     * 加载所有配置文件
     */
    public static void loadAll() {
        List<String> configurationFileList = SpringUtils.getConfigurationFileList();
        if (configurationFileList == null) {
            throw new IllegalStateException("configurationFileList未成功获取");
        }

        //查找激活的profile
        List<Object> activeProfileNameList = new ArrayList<>();
        String activeProfileKey = "spring.profiles.active";
        DefaultResourceLoader defaultResourceLoader = new DefaultResourceLoader();
        for (int i = configurationFileList.size() - 1; i >= 0; i--) {
            String configurationFile = configurationFileList.get(i);
            Resource resource = defaultResourceLoader.getResource(configurationFile);
            try {
                load(configurationFile, resource, null);
                //配置的单profile
                if (get(activeProfileKey) != null) {
                    String activeProfileName = get(activeProfileKey) + "";
                    activeProfileNameList.add(activeProfileName);
                    sourceMap.clear();
                    break;
                } else if (getList(activeProfileKey) != null && getList(activeProfileKey).size() > 0) {
                    activeProfileNameList = getList(activeProfileKey);
                    sourceMap.clear();
                    break;
                }
            } catch (Exception e) {
                log.trace("加载配置文件失败: [{}]", configurationFile, e);
            }
        }
        log.info("activeProfileNameList: {}", activeProfileNameList);

        //读取配置
        for (int i = 0; i < configurationFileList.size(); i++) {
            String configurationFile = configurationFileList.get(i);
            Resource resource = defaultResourceLoader.getResource(configurationFile);
            log.debug("加载配置文件: [{}]", configurationFile);
            load(configurationFile, resource, null);
            for (Object activeProfileName : activeProfileNameList) {
                load(configurationFile, resource, activeProfileName + "");
            }
        }
    }

    /**
     * 加载配置文件
     *
     * @param name
     * @param resource
     * @param profile
     */
    private static void load(String name, Resource resource, String profile) {
        try {
            MapPropertySource mapPropertySource = (MapPropertySource) new YamlPropertySourceLoader()
                    .load(name, resource, profile);
            if (mapPropertySource == null) {
                return;
            }
            Map<String, Object> sourceOne = mapPropertySource.getSource();
            log.trace("name: [{}], source: [{}], profile: [{}]", name, sourceOne, profile);
            sourceMap.putAll(sourceOne);
            log.trace("sourceMap after marge: [{}]", sourceMap);
        } catch (Exception e) {
            log.trace("加载配置文件失败: [{}]", name, e);
        }
    }

    /**
     * 获取对象
     *
     * @param key
     * @return
     */
    public static Object get(String key) {
        Object object = sourceMap.get(key);
        if (object == null) {
            return null;
        }

        //解析内部变量
        return parseVariable(object);
    }

    /**
     * 获取对象
     *
     * @param key
     * @return
     */
    public static String getString(String key) {
        return get(key) + "";
    }

    /**
     * 获取对象
     *
     * @param key
     * @return
     */
    public static int getInteger(String key) {
        return Integer.parseInt(getString(key));
    }

    /**
     * 获取对象
     *
     * @param key
     * @return
     */
    public static long getLong(String key) {
        return Long.parseLong(getString(key));
    }

    /**
     * 获取对象
     *
     * @param key
     * @return
     */
    public static boolean getBoolean(String key) {
        return Boolean.parseBoolean(getString(key));
    }

    /**
     * 获取对象
     *
     * @param key
     * @return
     */
    public static float getFloat(String key) {
        return Float.parseFloat(getString(key));
    }

    /**
     * 获取对象
     *
     * @param key
     * @return
     */
    public static double getDouble(String key) {
        return Double.parseDouble(getString(key));
    }

    /**
     * 获取对象
     *
     * @param key
     * @return
     */
    public static Map<String, Object> getMap(String key) {
        Set<String> keySet = sourceMap.keySet();
        List<String> matchedKeyList = keySet.stream()
                .filter(tmpKey -> {
                    if (tmpKey.startsWith(key) && !tmpKey.equals(key)) {
                        String tail = tmpKey.substring(key.length() + 1);
                        if (tail.indexOf("\\.") == -1) {
                            return true;
                        }
                    }
                    return false;
                })
                .collect(Collectors.toList());

        Map<String, Object> map = new HashMap<>();
        for (String matchKey : matchedKeyList) {
            String retKey = matchKey.substring(key.length() + 1).toLowerCase();
            map.put(retKey, parseVariable(sourceMap.get(matchKey)));
        }
        return map;
    }

    /**
     * 获取对象
     *
     * @param mapKey
     * @param keyInMap
     * @return
     */
    public static Object getValueInMap(String mapKey, String keyInMap) {
        Map<String, Object> map = getMap(mapKey);
        if (map == null) {
            throw new IllegalStateException("can not found map with key: " + mapKey);
        }
        return map.get(keyInMap.toLowerCase());
    }

    /**
     * 获取对象
     *
     * @param key
     * @return
     */
    public static List<Object> getList(String key) {
        Set<String> keySet = sourceMap.keySet();
        List<String> matchedKeyList = keySet.stream()
                .filter(tmpKey -> {
                    if (tmpKey.startsWith(key)) {
                        String tail = tmpKey.substring(key.length());
                        if (tail.matches("\\[\\d+\\]")) {
                            return true;
                        }
                    }
                    return false;
                })
                .collect(Collectors.toList());

        List<Object> objectList = new ArrayList<>();
        for (String matchKey : matchedKeyList) {
            objectList.add(sourceMap.get(matchKey));
        }
        return objectList;
    }

    /**
     * 获取对象
     *
     * @param key
     * @return
     */
    public static String[] getArray(String key) {
        return getString(key).split(",");
    }

    /**
     * 解析变量
     *
     * @param object
     * @return
     */
    private static Object parseVariable(Object object) {
        String objectString = object.toString();
        int indexHead = objectString.indexOf(HEAD);
        while (indexHead != -1) {
            int indexTail = objectString.indexOf(TAIL, indexHead);
            String variableName = objectString.substring(indexHead + HEAD.length(), indexTail);
            Object variableValue = sourceMap.get(variableName);
            objectString = objectString.substring(0, indexHead) + variableValue + objectString.substring(indexTail + TAIL.length());
            indexHead = objectString.indexOf(HEAD);
        }
        return objectString;
    }

    /**
     * 检查系统根路径是否是windows格式,是则报错
     *
     * @return
     */
    public static void checkRootParentPath() {
        String rootParentPath = getString("dwbi.base-common.root-parent-path");
        if (!rootParentPath.startsWith("/")) {
            throw new IllegalStateException("rootParentPath should be matches unix path. rootParentPath: " + rootParentPath);
        }
    }


}

 

     

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值