利用META-INF/spring.factories实现springboot自定义启动配置管理

52 篇文章 1 订阅

介绍

       微服务化是的我们需要管理的服务增多。同时每一个服务有各自的配置,对于同一个配置来说,开发、应用不同时刻所用的文件不同,这样会导致配置的维护更加的复杂和困难。spring cloud config可以实现统一的配置管理,但是在很多情况下不是很方便。我们需要根据自己的需要来定义的自己的启动配置。参照spring-cloud的方式,在这里实现一种自定义的服务启动配置管理。区别是加入自定义的配置解析方式。

记录好公共的配置信息(consul、mq等),以及各个服务私有配置定信息。公共的配置信息是不变的, 私有配置定信可以在各个微服务安装之前就定义好。安装的时候就告诉配置管理服务,配置管理服务使用界面展示这些信息,是用户可配置。在配置成功之后重启这些服务即可。

原理

使用PropertiesPropertySource类,在启动时更改环境变量,并不修改application.*(properties/yml)中的配置。

启动顺序:bootstrap.* > propertiesPropertySource > application.*

优先级:propertiesPropertySource > bootstrap.* > application.*

即同样的配置项,在propertiesPropertySource和application.properties都配置过的,会优先使用propertiesPropertySource中的配置。

实现

        采用spring.factories配置PropertySourceLocator的配置实现类。在程序启动的时候解析和获取自定义的配置信息。首先startupConfiig.xml中定义了项目依赖的配置项,例如数据配置信息、consul、redis等。而ConfigLocalPropertySourceLocator 中定义了解析startupConfiig.xml内容根据内容的定义去config.properties中获取相应的配置信息(上下文路径、端口、数据配置信息、consul、redis等)并将信息加载到项目中。config.properties的配置是预先设置好的,由统一配置管理按照一定的规则生成。当这里我们也不一定要解析config.properties的内容获取信息,也可以通过远程调用的方式来获取配置信息。总之就看实际需要实现即可。

startupConfiig.xml文件内容:

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <!--实际应用中使用的配置   config.properties中的配置项是 [instances.service].[instance.service].[instance.order]  获取-->
    <instances service="springDemoService">
        <!-- 服务实例相关设置 -->
        <instance type="service" name="service" order="1"/>
        <!-- 数据库相关设置   使用数据库配置为 db-->
        <instance type="database" name="db" order="1" dbType=""/>
        <!-- mq相关设置-->
        <instance type="rabbitmq" name="rabbitmq" order="1"/>
        <!-- consul相关设置-->
        <instance type="consul" name="consul" order="1"/>
        <!-- 缓存配置-->
        <instance type="cache" name="cache" order="1"/>
    </instances>
    <!--实际使用中的一些参数-->
    <parameters service="springDemoService">
        <!--  envKey:加载到环境中的数据的key值    key:config配置文件中的后缀    defaultValues:默认值,以“,”隔开,  order:取值的顺序 -->
        <parameter envKey="testKey1"  key="testKey1" defaultValues="testValue1" order="1"/>
        <parameter envKey="testKey1"  key="testKey2" defaultValues="testValue2" order="1"/>
        <parameter envKey="testKey1"  key="testKey3" defaultValues="testValue3" order="1"/>
    </parameters>
</config>

config.properties内容:

#使用的配置设置
springDemoService-cache.@instanceList=springDemoService-cache
springDemoService-db.@instanceList=springDemoService-db
springDemoService-service.@instanceList=springDemoService-service
springDemoService-consul.@instanceList=springDemoService-consul
#服务配置
springDemoService-service.@context=/spring-demo
springDemoService-service.@logLevel=DEBUG
springDemoService-service.@indexCode=8109ea47-7386-4704-8dce-5a3443cc777f
springDemoService-service.webPort=8081
springDemoService-service.@ip=10.16.65.13
#数据库配置
springDemoService-db.1.instance=test
springDemoService-db.1.@dbname=test_db
springDemoService-db.1.@version=1.1.1
springDemoService-db.1.@type=postgresql
springDemoService-db.1.@indexCode=00753ba3-857c-490a-be01-376ac36a82cf
springDemoService-db.1.@dbpassword=cG9zdGdyZXM=
springDemoService-db.1.@dbusername=test
springDemoService-db.1.port=5432
springDemoService-db.1.@ip=localhost
#注册中心配置
springDemoService-consul.1.@context=/VIID
springDemoService-consul.1.@logLevel=DEBUG
springDemoService-consul.1.@indexCode=8109ea47-7386-4704-8dce-5a3443cc777f
springDemoService-consul.1.webPort=8068
springDemoService-consul.1.@ip=10.19.155.2
#缓存配置
springDemoService-cache.1.@version=1.2.0
springDemoService-cache.1.@indexCode=11543d67-7824-4c57-b1b7-04d954919b89
springDemoService-cache.1.@type=redislinux64
springDemoService-cache.1.@password=MTIzNDU2
springDemoService-cache.1.@username=
springDemoService-cache.1.@ip=127.0.0.1
springDemoService-cache.1.port=6379

spring.factories配置:

# Bootstrap components
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
zhong.test.springbootdemo.usultestdemo.configration.propertiespropertysource.ConfigLocalConfiguration

配置加载:

@Configuration
public class ConfigLocalConfiguration {
    public ConfigLocalConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean({ConfigLocalPropertySourceLocator.class})
    public ConfigLocalPropertySourceLocator configLocalPropertySourceLocator() {
        return new ConfigLocalPropertySourceLocator();
    }
}

实现了程序中依赖的各种配置的获取方式。

配置是否生效判断

ConfigLocalPropertySourceLocator的locate(Environment environment)方法中我们需要实现自定义的配置逻辑,

 @SneakyThrows
    @Override
    public PropertySource<?> locate(Environment environment) {
       //记录最终加载到运行环境中的数据
        Properties localConfig = new Properties();
        if (init()) {
            propertyConvertor(localConfig);
        } else {
            LOGGER.warn("ConfigLocalPropertySourceLocator init failed!");
        }

        decryConfig(localConfig);
        //记录最终加载到运行环境中的数据

        return new PropertiesPropertySource("localConfig", localConfig);
    }

其中init()方法是是获取是否存在startupCoonfig.xml和config.properties文件存在,如果存在startupCoonfig.xml则继续解析congig.properties中的内容

/**
     * 初始化startupConfig.xml的配置信息
     * 将config.properties的配置信息加载到变量中。
     * @return
     */
    boolean init() {
        File configDir = null;
        if (null == this.startupConfigFile) {
            try {
                //获取 startupConfig.xml 的位置
                setStartupConfigXml();
                //如果配置存在就加载config.properties. 兼容本地开发环境,开发时不需要使用 startupConfig.xml 和 config.properties
                if(startupConfigFile != null){
                    //获取 config.properties 的位置
                    configDir = getConfigPropertyPath();
                    //加载config.properties中的数据
                    loadConfig(configDir);
                }
            } catch (FileNotFoundException var3) {
                LOGGER.error("file don't exists", var3);
                System.exit(-1);
            } catch (Exception var4) {
                LOGGER.error("startup-config init failed", var4);
            }
        }
        return null != this.configroperties;
    }

/**
     * 加载指定路径的 config.properties
     * @param configDir 文件
     */
    private void loadConfig(File configDir) {
        File configFile = null;
        configFile = new File(configDir.getAbsolutePath() + "/" + "config.properties");
        if (!configFile.exists()) {
            try {
                configFile = ResourceUtils.getFile("classpath:config.properties");
            } catch (IOException e) {
                LOGGER.error("config.properties don't exists", e);
                System.exit(-1);
            }
        }

        try {
            this.propertiesLoad(configFile, "config.properties", this.configroperties);
        } catch (Exception e) {
            LOGGER.error("load config.properties failed, target key don't exists", e);
            System.exit(-1);
        }

    }

/**
     * 获取 startupConfig.xml 的位置
     * @throws Exception 异常
     */
    private void setStartupConfigXml() throws Exception {
        ClassPathResource startupConfigXmlRes = new ClassPathResource("startupConfig.xml");

        try {
            this.startupConfigFile = startupConfigXmlRes.getFile();
        } catch (IOException var3) {
            LOGGER.error("get startupConfig.xml error. program will exit", var3);
            throw new Exception("startupConfig.xml does not exists, configuration of application.properties be used");
        }

        if (!this.startupConfigFile.exists()) {
            LOGGER.error("startupConfig.xml does not exist in classpath, configuration of application.properties be used");
            throw new Exception("startupConfig.xml does not exists");
        } else {
            LOGGER.info(String.format("StartupConfig.xml File exists, path=[%s]: ", this.startupConfigFile));
        }
    }

    /**
     * 指定 config.properties文件的位置 , 项目的安装位置的  /conf/config.properties   开发时可为空
     * @return
     */
    private File getConfigPropertyPath(){
        String userDir = System.getProperty("user.dir");
        File serviceDir = new File(userDir);
        LOGGER.info(String.format("userDir=[%s]",serviceDir.getAbsolutePath()));
        if (serviceDir.exists() && serviceDir.isDirectory()) {
            File componentDir;
            try {
                componentDir = this.getComponentDir(serviceDir);
            } catch (NullPointerException var6) {
                componentDir = new File(System.getProperty("user.dir"));
            }
            //生产环境下面的路径是需要有的。应该默认创建或者安装的时候创建
            if (componentDir.exists() && componentDir.isDirectory()) {
                String componentConfigDir = StringUtils.replace(componentDir.getAbsolutePath() + "/conf", "//", "/");
                LOGGER.info(String.format("componentConfigDir=[]", componentConfigDir));
                File componentConfigFile = new File(componentConfigDir);
                if (componentConfigFile.exists() && componentConfigFile.isDirectory()) {
                    return componentConfigFile;
                }
                LOGGER.error("componentInstallPath/conf/ don't exists");
            } else {
                LOGGER.error("componentInstallPath/conf/ don't exists");
            }
        } else {
            LOGGER.error("user.dir don't exists");
        }
        //走到这里说明是开发环境
        return new File(Thread.currentThread().getContextClassLoader().getResource(".").getPath());
    }
 /**
     * 获取项目的安装路径
     * @param serviceDir 服务路径
     * @return
     * @throws NullPointerException
     */
    private File getInstallDir(File serviceDir) throws NullPointerException {
        File componentDir = new File(System.getProperty("user.dir"));

        try {
            componentDir = serviceDir.getParentFile().getParentFile().getParentFile();
        } catch (Exception var4) {
            LOGGER.error("get component path error, but the program go on", var4);
        }

        LOGGER.info(String.format("componentDir=[]", componentDir.getAbsolutePath()));
        return componentDir;
    }

propertyConverter负责解析startupCoonfig.xml中的内容,包括项目使用配置(上下文路径、端口、数据配置信息、consul、redis等),以及自定义的变量内容。

/**
     * 解析 startupConfig.xml中的项
     * @param localConfig
     */
    void propertyConverter(Properties localConfig) {
        try {
            SAXReader sax = new SAXReader();
            Document dom = sax.read(this.startupConfigFile);
            Element configRoot = dom.getRootElement();
            Iterator iterator = configRoot.elements().iterator();

            //解析所有节点
            while (iterator.hasNext()) {
                Element element = (Element) iterator.next();
                String eleName = element.getName();
                //各种应用实例配置
                if (eleName.equals("instances")) {
                    //依赖配置解析实现
                    InstanceHandle.handle(element, localConfig, this.configroperties, this.myServiceInstance);
                }
                //动态参数配置
                if(eleName.equals("parameters")){
                    //配置参数解析实现
                    ParameterHandle.handle(element, localConfig, this.configroperties);
                }
                //其他需要可以自定义
            }

        } catch (DocumentException e) {
            LOGGER.error("Fail to parse file config.properties or startupConfig.xml exception", e);
        }
    }

依赖的服务解析

InstanceHandle 是对项目  上下文路径、端口、数据配置信息、consul、redis 的信息的解析。

/**
 * 服务依赖配置信息解析
 * @date 2020/12/16 - 16:23
 */
public class InstanceHandle {
    private static final Logger LOGGER = LoggerFactory.getLogger(InstanceHandle.class);

    public static void handle(Element insNode, Properties localConfig, Properties configs, ServiceInstance myServiceInstance) {
        String servicePrefix = insNode.attributeValue("service");

        if (StringUtils.isNotEmpty(servicePrefix)) {
            Iterator iterator = insNode.elements().iterator();
            while (iterator.hasNext()) {
                Element node = (Element) iterator.next();
                //获取 每个 <instance>节点的数据
                String insType = node.attributeValue("type");
                String insName = node.attributeValue("name");
                String keyOrder = node.attributeValue("order");
                //配置项
                String instancePrefix = servicePrefix + "-" + insName;
                //通过配置项获取配置的前缀
                String instanceValue = StringUtils.isNotBlank(insName) ? CommonTool.selectInstance(instancePrefix + ".@instanceList", keyOrder, configs) : null;
                //通过配置项解析每一个种类的配置数据
                switch (insType) {
                    case "consul":
                        //consul地址配置
                        TypeConsulHandle.handle(instanceValue, myServiceInstance, keyOrder, localConfig, configs);
                        break;
                    case "rabbitmq":
                        //rabbitmq地址配置
                        TypeRabbitmqHandle.handle(instanceValue, keyOrder, localConfig, configs);
                        break;
                    case "cache":
                        //redis配置
                        if (null != instanceValue) {
                            TypeCacheHandle.handle(instanceValue, configs, keyOrder, localConfig);
                        } else {
                            LOGGER.warn(String.format("Lack of instance list name of type=[%s] in config.properties!", insType));
                        }
                        break;
                    case "database":
                        //数据库配置
                        String plugin = node.attributeValue("plugin");
                        String pgdialect = node.attributeValue("pgdialect");
                        String oracledialect = node.attributeValue("oracledialect");
                        if (null != instanceValue) {
                            TypeDataBaseHandle.handle(localConfig, configs, plugin, instanceValue, keyOrder, pgdialect, oracledialect);
                        } else {
                            LOGGER.warn(String.format("Lack of instance list name of type=[%s] in config.properties!", insType));
                        }
                        break;
                    case "service":
                        //服务配置
                        System.setProperty("spring.session.redis.namespace", insName);
                        myServiceInstance.setServiceId(instancePrefix);
                        if (null != instanceValue) {
                            TypeServiceHandle.handle(instanceValue, myServiceInstance, localConfig, configs);
                        } else {
                            LOGGER.warn(String.format("Lack of instance list name of type=[%s] in config.properties! ", insType));
                        }
                        break;
                    default:
                        LOGGER.warn(String.format("can't support instance type,type=[%s]: ", insType));
                }
            }

        } else {
            LOGGER.error(String.format("cannot loading the service prefix from the node [instances]"));
            System.exit(-1);
        }
    }
}

下面展示项目的配置(端口、上下文路径,端口等)和数据库配置

服务配置信息解析:

public class TypeServiceHandle {
    private static final Logger LOGGER = LoggerFactory.getLogger(TypeServiceHandle.class);

    public TypeServiceHandle() {
    }

    public static void handle(String instanceValue, ServiceInstance myServiceInstance, Properties localConfig, Properties bicConfigs) {
        String bicValue = null;
        myServiceInstance.setInstanceValue(instanceValue);
        bicValue = bicConfigs.getProperty(instanceValue + ".webPort");
        CommonTool.setLocalConfigAndMyServiceInstance("server.port", bicValue, myServiceInstance::setWebPort, localConfig, instanceValue + ".webPort");
        bicValue = bicConfigs.getProperty(instanceValue + ".@context");
        CommonTool.setLocalConfigAndMyServiceInstance("server.context-path", bicValue, myServiceInstance::setContext, localConfig, instanceValue + ".@context");
        CommonTool.setLocalConfigAndMyServiceInstance("server.servlet.context-path", bicValue, myServiceInstance::setContext, localConfig, instanceValue + ".@context");
        bicValue = bicConfigs.getProperty(instanceValue + ".@ip");
        CommonTool.setLocalConfigAndMyServiceInstance("server.ip", bicValue, myServiceInstance::setAddress, localConfig, instanceValue + ".@ip");
        myServiceInstance.setIndexCode(bicConfigs.getProperty(instanceValue + ".@indexCode"));
    }
}

数据库信息解析

兼容了oracle和pg的配置,也可以加入mysql等

public class TypeDataBaseHandle {
    private static final Logger LOGGER = LoggerFactory.getLogger(TypeDataBaseHandle.class);

    public static void handle(Properties localConfig, Properties bicConfigs, String plugin, String dbInstance, String keyOrder, String pgDialect, String oracleDialect) {
        String keyPrefix = dbInstance + "." + keyOrder + ".";

        String dbtype = bicConfigs.getProperty(keyPrefix + "@type");

        String dbIp = CommonTool.getFirstIp(bicConfigs.getProperty(keyPrefix + "@ip"));
        String dbPort = bicConfigs.getProperty(keyPrefix + "port");
        String dbUsername = bicConfigs.getProperty(keyPrefix + "@dbusername");
        String dbPassword = bicConfigs.getProperty(keyPrefix + "@dbpassword");
        String dbName = bicConfigs.getProperty(keyPrefix + "@dbname");

        //oracle使用
        String dbInstanceName = bicConfigs.getProperty(keyPrefix + "instance");
        //测试是否可用
        CommonTool.portIsAvailableOrExit(dbIp, dbPort, "db");
        if (StringUtils.isNoneEmpty(new CharSequence[]{dbtype, dbPort, dbName, dbUsername, dbPassword})) {
            localConfig.setProperty("spring.datasource.username", dbUsername);
            localConfig.setProperty("spring.datasource.password", CommonTool.decode(dbPassword));
            byte dbPlugins;
            if (StringUtils.contains(dbtype, "postgresql")) {
                //使用pg 数据库
                localConfig.setProperty("spring.datasource.name", "postgresql");
                if (StringUtils.isNotBlank(plugin)) {
                    if (plugin.hashCode() == 102353 && plugin.equals("gis")) {
                        localConfig.setProperty("spring.jpa.properties.hibernate.dialect", "org.hibernate.spatial.dialect.postgis.PostgisDialect");
                    } else {
                        localConfig.setProperty("spring.jpa.properties.hibernate.dialect", "org.hibernate.dialect.PostgreSQL9Dialect");
                    }
                } else {
                    localConfig.setProperty("spring.jpa.properties.hibernate.dialect", "org.hibernate.dialect.PostgreSQL9Dialect");
                }

                if (StringUtils.isNotBlank(pgDialect)) {
                    localConfig.setProperty("spring.jpa.properties.hibernate.dialect", pgDialect);
                }

                localConfig.setProperty("spring.datasource.url", "jdbc:postgresql://" + dbIp + ":" + dbPort + "/" + dbName);
                localConfig.setProperty("spring.datasource.driver-class-name", "org.postgresql.Driver");
                localConfig.setProperty("spring.datasource.validationQuery", "SELECT 1");
            } else if (StringUtils.contains(dbtype, "oracle")) {
                //使用oracle 方式连接数据库
                localConfig.setProperty("spring.datasource.name", "oracle");
                if (StringUtils.isNotBlank(plugin)) {
                         if (plugin.equals("gis")) {
                             localConfig.setProperty("spring.jpa.properties.hibernate.dialect", "org.hibernate.spatial.dialect.oracle.OracleSpatial11gDialect");
                         } else {
                             localConfig.setProperty("spring.jpa.properties.hibernate.dialect", "org.hibernate.dialect.Oracle10gDialect");
                         }
                } else {
                    localConfig.setProperty("spring.jpa.properties.hibernate.dialect", "org.hibernate.dialect.Oracle10gDialect");
                }

                if (StringUtils.isNotBlank(oracleDialect)) {
                    localConfig.setProperty("spring.jpa.properties.hibernate.dialect", oracleDialect);
                }

                localConfig.setProperty("spring.datasource.url", "jdbc:oracle:thin:@" + dbIp + ":" + dbPort + ":" + dbInstanceName);
                localConfig.setProperty("spring.datasource.driver-class-name", "oracle.jdbc.OracleDriver");
                localConfig.setProperty("spring.datasource.validationQuery", "select 'x' FROM DUAL");
            } else {
                LOGGER.warn("Error in database @type in config.properties");
            }
        } else {
            LOGGER.warn("Lack of some database configurations in config.properties");
        }
        LOGGER.debug(String.format("Database configuration: dbUrl=[]  : ", localConfig.getProperty("spring.datasource.url", "")));
    }
}

consul信息配置解析

public class TypeConsulHandle {
    private static final Logger LOGGER = LoggerFactory.getLogger(TypeConsulHandle.class);

    public static void handle(String instanceValue, ServiceInstance myServiceInstance, String keyOrder, Properties localConfig, Properties bicConfigs) {
        LOGGER.debug(String.format("Loading consul configurations from config.properties, instance= [%s]", instanceValue));
        localConfig.setProperty("spring.cloud.consul.host", bicConfigs.getProperty(instanceValue + "." + keyOrder + ".@ip"));
        localConfig.setProperty("spring.cloud.consul.port", bicConfigs.getProperty(instanceValue + "." + keyOrder + ".webPort"));

        localConfig.setProperty("spring.cloud.consul.discovery.preferIpAddress", "true");
        if (StringUtils.isNotBlank(myServiceInstance.getAddress())) {
            localConfig.setProperty("spring.cloud.consul.discovery.ip-address", CommonTool.getFirstIp(myServiceInstance.getAddress()));
            if (StringUtils.isNotBlank(myServiceInstance.getContext())) {
                localConfig.setProperty("spring.cloud.consul.discovery.health-check-path", myServiceInstance.getContext() + "/health");
            } else {
                localConfig.setProperty("spring.cloud.consul.discovery.health-check-path", "/health");
            }
        } else {
            LOGGER.warn("Cannot set consul.discovery.ip-address, please set your service instance in startupConfig.xml");
        }

        localConfig.setProperty("spring.cloud.consul.discovery.health-check-interval", "20s");
        Optional<String> instanceIndexCode = Optional.ofNullable(myServiceInstance.getIndexCode());
        instanceIndexCode.ifPresent((s) -> {
            localConfig.setProperty("spring.cloud.consul.discovery.instance-id", myServiceInstance.getServiceId() + "-" + s);
        });
        CommonTool.portIsAvailableOrExit(localConfig.getProperty("spring.cloud.consul.host", ""), localConfig.getProperty("spring.cloud.consul.port", ""), "consul");
    }
}

redis信息配置解析

public class TypeCacheHandle {
    private static final Logger LOGGER = LoggerFactory.getLogger(TypeCacheHandle.class);

    public TypeCacheHandle() {
    }

    /**
     * 获取缓存配置信息
     *
     * @param instanceValue 缓存前缀
     * @param bicConfigs    所有配置信息
     * @param keyOrder      使用的配置顺序
     * @param localConfig   本地配置项
     */
    public static void handle(String instanceValue, Properties bicConfigs, String keyOrder, Properties localConfig) {
        String bicValue = null;
        String keyPrefix = instanceValue + "." + keyOrder + ".";

        String ipKey = keyPrefix + "@ip";
        bicValue = bicConfigs.getProperty(ipKey);
        CommonTool.setLocalConfig("spring.redis.host", CommonTool.getFirstIp(bicValue), localConfig, ipKey);

        String portKey = keyPrefix + "port";
        bicValue = bicConfigs.getProperty(portKey);
        CommonTool.setLocalConfig("spring.redis.port", bicValue, localConfig, portKey);

        String pwdKey = keyPrefix + "@password";
        bicValue = bicConfigs.getProperty(pwdKey);
        CommonTool.setLocalConfig("spring.redis.password", CommonTool.decode(bicValue), localConfig, pwdKey);

        CommonTool.setLocalConfig("management.health.redis.enabled", "false", localConfig, "management.health.redis.enabled");
        CommonTool.portIsAvailableOrExit(localConfig.getProperty("spring.redis.host", ""), localConfig.getProperty("spring.redis.port", ""), "cache");
    }
}

       在上面的各种配置中都是解析的是config中有的配置信息。有时候我们完全不需要配置这些信息。我们只需要有一个配置中心提供这些信息(数据库、redis、consul、服务上下文和端口、mq)的查询接口,同时config.properties中记录了配置中心的ip和端口信息。而我们需要实现的是根据startupConfi.xml的配置,从config.properties取出服务ip和端口然后调用接口查询,最后加载到环境中。

依赖配置解析

参数的解析相对简单,只要对应解析出key、默认值、取值排序然后组装。最后放到环境中也可以。这种方式也可以通过远程从配置中心获取的方式。也可以提供界面做配置修改,然后保存到config.properties中,重启服务就可以加载到信息。

/**
 * 参数解析
 * @date 2020/12/18 - 17:20
 */
public class ParameterHandle {
    private static final Logger LOGGER = LoggerFactory.getLogger(InstanceHandle.class);

    /**
     * 解析各种参数配置
     * @param element 参数配置节点
     * @param localConfig 加载到环境中的配置项
     * @param configs config.properties的值
     */
    public static void handle(Element element, Properties localConfig, Properties configs){
        Iterator<Element> iterator = element.elements().iterator();
        String paramPrefix = element.attributeValue("service");
        if(StringUtils.isNotEmpty(paramPrefix)) {
            while (iterator.hasNext()) {
                Element subElement = iterator.next();
                String keyName = subElement.attributeValue("key");
                String defaultValues = subElement.attributeValue("defaultValues");
                String envKey = subElement.attributeValue("envKey");
                int order = StringUtils.isEmpty(subElement.attributeValue("order")) ? 1 : Integer.valueOf(subElement.attributeValue("order"));
                String[] values = defaultValues.split(",");
                String defaultValue = values.length >= order ? values[order - 1] : null;
                setValue(paramPrefix+ "."+ keyName, configs, localConfig, order, defaultValue, envKey);
            }
        }else {
            LOGGER.warn("Fail to get the value of [service] from node [parameters]");
        }
    }

    private static void setValue(String instanceName, Properties localConfig, Properties configs, int order, String defaultValue, String envKey){
        String instanceNameValues = configs.getProperty(instanceName + "." + order);
        if(StringUtils.isNotEmpty(instanceNameValues)) {
            String[] values = instanceNameValues.split(",");
            String value = values.length > order ? values[order - 1] : null;
            if (StringUtils.isNotEmpty(value)) {
                localConfig.setProperty(envKey, value);
            } else if (StringUtils.isNotEmpty(defaultValue)) {
                localConfig.setProperty(envKey, defaultValue);
            } else {
                LOGGER.warn(String.format("Value of [%s] order [%s] is null. setting of config.properties is [%s]. default value is [%s]", envKey, order, instanceNameValues, defaultValue));
            }
        } else if(StringUtils.isNotEmpty(defaultValue)){
            localConfig.setProperty(envKey, defaultValue);
        } else {
            LOGGER.warn(String.format("Value of [%s] order [%s] is null. setting of config.properties is [%s]. default value is [%s]", envKey, order, instanceNameValues, defaultValue));
        }
    }
}

 

https://blog.csdn.net/f641385712/article/details/84401629

https://www.cnblogs.com/lizo/p/7683300.html

https://blog.csdn.net/qq_31086797/article/details/107500079

https://www.cnblogs.com/zhangjianbin/p/6322476.html

https://cloud.tencent.com/developer/article/1497673

https://www.imooc.com/article/299972

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值