我的编程空间,编程开发者的网络收藏夹
学习永远不晚

SpringBoot动态更新yml文件

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

SpringBoot动态更新yml文件

前言

在系统运行过程中,可能由于一些配置项的简单变动需要重新打包启停项目,这对于在运行中的项目会造成数据丢失,客户操作无响应等情况发生,针对这类情况对开发框架进行升级提供yml文件实时修改更新功能

项目依赖

项目基于的是2.0.0.RELEASE版本,所以snakeyaml需要单独引入,高版本已包含在内

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

网上大多数方法是引入spring-cloud-context配置组件调用ContextRefresher的refresh方法达到同样的效果,考虑以下两点未使用

  • 开发框架使用了logback日志,引入spring-cloud-context会造成日志配置读取错误
  • 引入spring-cloud-context会同时引入spring-boot-starter-actuator组件,会开放一些健康检查路由及端口,需要对框架安全方面进行额外控制

YML文件内容获取

读取resource文件下的文件需要使用ClassPathResource获取InputStream

    public String getTotalYamlFileContent() throws Exception {
        String fileName = "application.yml";
        return getYamlFileContent(fileName);
    }
    public String getYamlFileContent(String fileName) throws Exception {
        ClassPathResource classPathResource = new ClassPathResource(fileName);
        return onvertStreamToString(classPathResource.getInputStream());
    }
    public static String convertStreamToString(InputStream inputStream) throws Exception{
       return IOUtils.toString(inputStream, "utf-8");
    }

YML文件内容更新

我们获取到yml文件内容后可视化显示到前台进行展示修改,将修改后的内容通过yaml.load方法转换成Map结构,再使用yaml.dumpAsMap转换为流写入到文件

    public void updateTotalYamlFileContent(String content) throws Exception {
        String fileName = "application.yml";
        updateYamlFileContent(fileName, content);
    }
	public void updateYamlFileContent(String fileName, String content) throws Exception {
        Yaml template = new Yaml();
        Map<String, Object> yamlMap = template.load(content);
        ClassPathResource classPathResource = new ClassPathResource(fileName);
        Yaml yaml = new Yaml();
        //字符输出
        FileWriter fileWriter = new FileWriter(classPathResource.getFile());
        //用yaml方法把map结构格式化为yaml文件结构
        fileWriter.write(yaml.dumpAsMap(yamlMap));
        //刷新
        fileWriter.flush();
        //关闭流
        fileWriter.close();
    }

YML属性刷新

yml属性在程序中读取使用一般有三种

使用Value注解

    @Value("${system.systemName}")
    private String systemName;

通过enviroment注入读取

    @Autowired
    private Environment environment;
    environment.getProperty("system.systemName")

使用ConfigurationProperties注解读取

@Component
@ConfigurationProperties(prefix = "system")
public class SystemConfig {
    private String systemName;
}

Property刷新

我们通过environment.getProperty方法读取的配置集合实际是存储在PropertySources中的,我们只需要把键值对全部取出存储在propertyMap中,将更新后的yml文件内容转换成相同格式的ymlMap,两个Map进行合并,调用PropertySources的replace方法进行整体替换即可

但是yaml.load后的ymlMap和PropertySources取出的propertyMap两者数据解构是不同的,需要进行手动转换

propertyMap集合就是单纯的key,value键值对,key是properties形式的名称,例如system.systemName=>xxxxx集团管理系统

ymlMap集合是key,LinkedHashMap的嵌套层次结构,例如system=>(systemName=>xxxxx集团管理系统)

转换方法如下

  public HashMap<String, Object> convertYmlMapToPropertyMap(Map<String, Object> yamlMap) {
        HashMap<String, Object> propertyMap = new HashMap<String, Object>();
        for (String key : yamlMap.keySet()) {
            String keyName = key;
            Object value = yamlMap.get(key);
            if (value != null && value.getClass() == LinkedHashMap.class) {
                convertYmlMapToPropertyMapSub(keyName, ((LinkedHashMap<String, Object>) value), propertyMap);
            } else {
                propertyMap.put(keyName, value);
            }
        }
        return propertyMap;
    }
    private void convertYmlMapToPropertyMapSub(String keyName, LinkedHashMap<String, Object> submMap, Map<String, Object> propertyMap) {
        for (String key : submMap.keySet()) {
            String newKey = keyName + "." + key;
            Object value = submMap.get(key);
            if (value != null && value.getClass() == LinkedHashMap.class) {
                convertYmlMapToPropertyMapSub(newKey, ((LinkedHashMap<String, Object>) value), propertyMap);
            } else {
                propertyMap.put(newKey, value);
            }
        }
    }

刷新方法如下

        String name = "applicationConfig: [classpath:/" + fileName + "]";
        MapPropertySource propertySource = (MapPropertySource) environment.getPropertySources().get(name);
        Map<String, Object> source = propertySource.getSource();
        Map<String, Object> map = new HashMap<>(source.size());
        map.putAll(source);
        Map<String, Object> propertyMap = convertYmlMapToPropertyMap(yamlMap);
        for (String key : propertyMap.keySet()) {
            Object value = propertyMap.get(key);
            map.put(key, value);
        }
        environment.getPropertySources().replace(name, new MapPropertySource(name, map));

注解刷新

不论是Value注解还是ConfigurationProperties注解,实际都是通过注入Bean对象的属性方法使用的,我们先自定注解RefreshValue来修饰属性所在Bean的class

通过实现InstantiationAwareBeanPostProcessorAdapter接口在系统启动时过滤筛选对应的Bean存储下来,在更新yml文件时通过spring的event通知更新对应

bean的属性即可

注册事件使用EventListener注解

    @EventListener
    public void updateConfig(ConfigUpdateEvent configUpdateEvent) {
        if(mapper.containsKey(configUpdateEvent.key)){
            List<FieldPair> fieldPairList = mapper.get(configUpdateEvent.key);
            if(fieldPairList.size()>0){
                for (FieldPair fieldPair:fieldPairList) {
                    fieldPair.updateValue(environment);
                }
            }
        }
    }

通知触发事件使用ApplicationContext的publishEvent方法

    @Autowired
    private ApplicationContext applicationContext;
  	for (String key : propertyMap.keySet()) {
       applicationContext.publishEvent(new YamlConfigRefreshPostProcessor.ConfigUpdateEvent(this, key));
    }

YamlConfigRefreshPostProcessor的完整代码如下

@Component
public class YamlConfigRefreshPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements EnvironmentAware {
    private Map<String, List<FieldPair>> mapper = new HashMap<>();
    private Environment environment;
    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        processMetaValue(bean);
        return super.postProcessAfterInstantiation(bean, beanName);
    }
    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
    private void processMetaValue(Object bean) {
        Class clz = bean.getClass();
        if (!clz.isAnnotationPresent(RefreshValue.class)) {
            return;
        }
        if (clz.isAnnotationPresent(ConfigurationProperties.class)) {
            //@ConfigurationProperties注解
            ConfigurationProperties config = (ConfigurationProperties) clz.getAnnotation(ConfigurationProperties.class);
            for (Field field : clz.getDeclaredFields()) {
                String key = config.prefix() + "." + field.getName();
                if(mapper.containsKey(key)){
                    mapper.get(key).add(new FieldPair(bean, field, key));
                }else{
                    List<FieldPair> fieldPairList = new ArrayList<>();
                    fieldPairList.add(new FieldPair(bean, field, key));
                    mapper.put(key, fieldPairList);
                }
            }
        } else {
            //@Valuez注解
            try {
                for (Field field : clz.getDeclaredFields()) {
                    if (field.isAnnotationPresent(Value.class)) {
                        Value val = field.getAnnotation(Value.class);
                        String key = val.value().replace("${", "").replace("}", "");
                        if(mapper.containsKey(key)){
                            mapper.get(key).add(new FieldPair(bean, field, key));
                        }else{
                            List<FieldPair> fieldPairList = new ArrayList<>();
                            fieldPairList.add(new FieldPair(bean, field, key));
                            mapper.put(key, fieldPairList);
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                System.exit(-1);
            }
        }
    }
    public static class FieldPair {
        private static PropertyPlaceholderHelper propertyPlaceholderHelper = new PropertyPlaceholderHelper("${", "}",
                ":", true);
        private Object bean;
        private Field field;
        private String value;
        public FieldPair(Object bean, Field field, String value) {
            this.bean = bean;
            this.field = field;
            this.value = value;
        }
        public void updateValue(Environment environment) {
            boolean access = field.isAccessible();
            if (!access) {
                field.setAccessible(true);
            }
            try {
                if (field.getType() == String.class) {
                    String updateVal = environment.getProperty(value);
                    field.set(bean, updateVal);
                }
                else if (field.getType() == Integer.class) {
                    Integer updateVal = environment.getProperty(value,Integer.class);
                    field.set(bean, updateVal);
                }
                else if (field.getType() == int.class) {
                    int updateVal = environment.getProperty(value,int.class);
                    field.set(bean, updateVal);
                }
                else if (field.getType() == Boolean.class) {
                    Boolean updateVal = environment.getProperty(value,Boolean.class);
                    field.set(bean, updateVal);
                }
                else if (field.getType() == boolean.class) {
                    boolean updateVal = environment.getProperty(value,boolean.class);
                    field.set(bean, updateVal);
                }
                else {
                    String updateVal = environment.getProperty(value);
                    field.set(bean, JSONObject.parseObject(updateVal, field.getType()));
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            field.setAccessible(access);
        }
        public Object getBean() {
            return bean;
        }
        public void setBean(Object bean) {
            this.bean = bean;
        }
        public Field getField() {
            return field;
        }
        public void setField(Field field) {
            this.field = field;
        }
        public String getValue() {
            return value;
        }
        public void setValue(String value) {
            this.value = value;
        }
    }
    public static class ConfigUpdateEvent extends ApplicationEvent {
        String key;
        public ConfigUpdateEvent(Object source, String key) {
            super(source);
            this.key = key;
        }
    }
    @EventListener
    public void updateConfig(ConfigUpdateEvent configUpdateEvent) {
        if(mapper.containsKey(configUpdateEvent.key)){
            List<FieldPair> fieldPairList = mapper.get(configUpdateEvent.key);
            if(fieldPairList.size()>0){
                for (FieldPair fieldPair:fieldPairList) {
                    fieldPair.updateValue(environment);
                }
            }
        }
    }
}

到此这篇关于SpringBoot动态更新yml文件的文章就介绍到这了,更多相关SpringBoot更新yml内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

SpringBoot动态更新yml文件

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

SpringBoot动态更新yml文件

在系统运行过程中,可能由于一些配置项的简单变动需要重新打包启停项目,这对于在运行中的项目会造成数据丢失,客户操作无响应等情况发生,针对这类情况对开发框架进行升级提供yml文件实时修改更新功能,这篇文章主要介绍了SpringBoot动态更新yml文件
2023-01-02

SpringBoot怎么动态更新yml文件

这篇文章主要介绍“SpringBoot怎么动态更新yml文件”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“SpringBoot怎么动态更新yml文件”文章能帮助大家解决问题。项目依赖项目基于的是2.
2023-07-04

SpringBoot怎么动态修改yml配置文件

本篇内容介绍了“SpringBoot怎么动态修改yml配置文件”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!前言记录下SpringBoot修
2023-06-29

Springboot的yml配置文件用法

这篇文章主要介绍了Springboot的yml配置文件用法,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2023-03-19

springboot怎么配置多个yml文件

在Spring Boot中配置多个YAML文件可以通过以下步骤实现:创建多个YAML文件,每个文件对应一个配置文件。例如,创建一个名为application.yml的默认配置文件和一个名为application-dev.yml的开发环境配置
springboot怎么配置多个yml文件
2024-02-29

SpringBoot yml配置文件如何读取

本篇内容主要讲解“SpringBoot yml配置文件如何读取”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“SpringBoot yml配置文件如何读取”吧!yaml介绍YAML(YAML Ai
2023-07-04

springboot怎么读取yml文件内容

在Spring Boot中,可以使用@ConfigurationProperties注解来读取yml文件中的内容。首先,在你的Spring Boot应用程序中创建一个配置类,使用@Configuration注解标记,然后使用@Configu
2023-10-18

SpringBoot实现读取YML,yaml,properties文件

yml,yaml,properties三种文件都是用来存放配置的文件,一些静态数据,配置的数据都会存放到里边。本文主要为大家整理了SpringBoot实现读取YML,yaml,properties文件的方法,需要的可以参考一下
2023-05-16

Springboot的yml配置文件怎么使用

今天小编给大家分享一下Springboot的yml配置文件怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。一、服务配置
2023-07-05

SpringBoot怎么实时刷新静态文件

小编给大家分享一下SpringBoot怎么实时刷新静态文件,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!方案Spring Boot在MVC中有个WebMvcProperties的配置类,还有个ResourcePropert
2023-06-22

Springboot如何通过yml配置文件为静态成员变量赋值

这篇文章主要介绍了Springboot如何通过yml配置文件为静态成员变量赋值,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2022-11-13

SpringBoot怎么实现读取YML,yaml,properties文件

本篇内容主要讲解“SpringBoot怎么实现读取YML,yaml,properties文件”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“SpringBoot怎么实现读取YML,yaml,pro
2023-07-06

SpringBoot读取yml文件有哪几种方式

这篇文章主要介绍了SpringBoot读取yml文件有哪几种方式,具有一定借鉴价值,需要的朋友可以参考下。下面就和我一起来看看吧。Spring Boot读取yml文件的主要方式有以下几种:1.@Value注解我们可以在bean的属性上使用@
2023-07-06

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录