【springboot配置项动态刷新】与【yaml文件转换为java对象】

文章目录

  • 一,序言
  • 二,准备工作
    • 1. pom.xml引入组件
    • 2. 配置文件示例
  • 三,自定义配置项动态刷新编码实现
    • 1. 定义自定义配置项对象
    • 2. 添加注解实现启动时自动注入
    • 3. 实现yml文件监听以及文件变化处理
  • 四,yaml文件转换为java对象
    • 1. 无法使用前缀绑定的处理
    • 2. 实现yaml文件转换java对象
  • 五、完整代码
    • 1. 代码结构
    • 2. 完整代码备份
    • 3. 运行说明

一,序言

springboot 配置文件一般以yaml方式保存,除了系统配置项如spring、server等外,还有我们自定义的配置项,方便系统启动时自动注入。

自定义的配置项一般是动态配置项,在系统运行过程中,可能需要在线修改,来实现自定义的配置项不停服更新,也就是类似于spring-cloud-starter-config的动态刷新。

由于系统不重启,无法通过自动注入的方式自动更新自定义配置, 这儿便需要我们手动加载yaml文件,转换为java对象,将变化赋值到spring管理的对象中

二,准备工作

采用最常见的snakeyaml、YAMLMapper来实现yaml文件处理。

1. pom.xml引入组件

因 jackson-dataformat-yaml 已经包含snakeyaml ,只需引入前者。

<dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-yaml</artifactId>
</dependency>

2. 配置文件示例

sample.yml

spring:datasource:url: ${druid.url}username: ${druid.username}password: ${druid.password}driverClassName: ${druid.driverClassName}type: com.alibaba.druid.pool.DruidDataSourcesqlScriptEncoding: utf-8schema: classpath:sql/schema.sqlcontinue-on-error: truedruid:initial-size: 5                                       # 初始化大小min-idle: 10                                          # 最小连接数max-active: 20                                        # 最大连接数max-wait: 60000                                       # 获取连接时的最大等待时间min-evictable-idle-time-millis: 300000                # 一个连接在池中最小生存的时间,单位是毫秒time-between-eviction-runs-millis: 60000              # 多久才进行一次检测需要关闭的空闲连接,单位是毫秒validation-query: SELECT 1                            # 检测连接是否有效的 SQL语句,为空时以下三个配置均无效test-on-borrow: true                                  # 申请连接时执行validationQuery检测连接是否有效,默认true,开启后会降低性能test-on-return: true                                  # 归还连接时执行validationQuery检测连接是否有效,默认false,开启后会降低性能test-while-idle: true                                 # 申请连接时如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效,默认false,建议开启,不影响性能devtools:restart:exclude: application-dev.yml,welcome.propertiesperson: name: qinjiangage: 18happy: falsebirth: 2000-01-01maps: {k1: v1,k2: v2}lists:- code- girl- musicdog:name: 旺财age: 1

三,自定义配置项动态刷新编码实现

1. 定义自定义配置项对象

import java.util.Date;
import java.util.List;
import java.util.Map;import lombok.Data;@Data
public class Person
{private String name;private Integer age;private Boolean happy;private Date birth;private Map<String, Object> maps;private List<Object> lists;private Dog dog;
}
import lombok.Data;@Data
public class Dog
{private String name;private Integer age;
}

2. 添加注解实现启动时自动注入

在Person类添加 @Component、@ConfigurationProperties(prefix = “person”) 实现自动注入,spring管理

@Data
@Component
@ConfigurationProperties(prefix = "person")
public class Person
{private String name;private Integer age;private Boolean happy;private Date birth;private Map<String, Object> maps;private List<Object> lists;private Dog dog;
}

3. 实现yml文件监听以及文件变化处理


/*** 监听文件变化(推荐)*/
@Slf4j
@Component
public class ReloadByFileAlterationMonitor
{@Autowiredprivate Welcome welcome;/*** thread-safe*/YAMLMapper yamlMapper = new YAMLMapper();/*** 初始化yml文件监听器*/@PostConstructpublic void initYamlMonitor(){try{URL url = ResourceUtils.getURL(ResourceUtils.CLASSPATH_URL_PREFIX);if (ResourceUtils.isFileURL(url)){FileAlterationObserver observer = new FileAlterationObserver(url.getPath(), FileFilterUtils.suffixFileFilter(".yml"));observer.addListener(new FileAlterationListenerAdaptor(){@Overridepublic void onFileChange(File file){log.info("★★★★★★★★ {} changed.", file.getName());if (StringUtils.equals("application-dev.yml", file.getName())){try{// yaml to JavaBeanString text = FileUtils.readFileToString(file, StandardCharsets.UTF_8);JavaBean javaBean = yamlMapper.readValue(text, JavaBean.class);if (javaBean != null && javaBean.getWelcome() != null){String value = javaBean.getWelcome().getMessage();log.info("#### autoRefresh to: {}", value);welcome.setMessage(value);}}catch (IOException e){log.error(e.getMessage(), e.getCause());}}}});long interval = TimeUnit.SECONDS.toMillis(10);FileAlterationMonitor monitor = new FileAlterationMonitor(interval, observer);monitor.start();}}catch (Exception e){log.error(e.getMessage(), e.getCause());}}
}

四,yaml文件转换为java对象

1. 无法使用前缀绑定的处理

定义Result 使用Person person绑定yaml中前缀为person的数据,为了避免报错,同时定义了Map<String, Object> spring 来保存spring节点数据。

import lombok.Data;/*** 定义Result实体绑定Person<br>* 与下面的Spring配置等价<br>* @Component<br>* @ConfigurationProperties(prefix = "person")<br>* public class Person { ... }*/
@Data
public class Result
{private Person person;private Map<String, Object> spring;
}

2. 实现yaml文件转换java对象

注意: org.yaml.snakeyaml.Yaml 非线程安全,建议使用 YAMLMapper


import java.io.IOException;
import java.nio.charset.StandardCharsets;import org.apache.commons.io.IOUtils;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import org.yaml.snakeyaml.Yaml;import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
import com.fly.refresh.entity.Result;import lombok.extern.slf4j.Slf4j;@Slf4j
public class SampleTest
{static String yamlText;YAMLMapper yamlMapper = new YAMLMapper();@BeforeClasspublic static void init(){try{yamlText = IOUtils.toString(new ClassPathResource("yaml/sample.yml").getURL(), StandardCharsets.UTF_8);log.info("yamlText => {}", yamlText);}catch (IOException e){log.error(e.getMessage(), e.getCause());}}/*** 解析带prefix的yaml*/@Testpublic void test()throws IOException{Result result = new Yaml().loadAs(yamlText, Result.class);log.info("snakeyaml  toJavaBean: {}", result);result = yamlMapper.readValue(yamlText, Result.class);log.info("yamlMapper toJavaBean: {}", result);} 
}

五、完整代码

1. 代码结构

在这里插入图片描述

2. 完整代码备份

如何使用下面的备份文件恢复成原始的项目代码,请移步查阅:神奇代码恢复工具

//goto docker\docker-compose.yml
version: '3'
services:hello:image: registry.cn-shanghai.aliyuncs.com/00fly/spring-config-refresh:1.0.0container_name: config-refreshdeploy:resources:limits:cpus: '1'memory: 300Mreservations:cpus: '0.05'memory: 200Mports:- 8080:8080environment:JAVA_OPTS: -server -Xms200m -Xmx200m -Djava.security.egd=file:/dev/./urandomrestart: on-failurelogging:driver: json-fileoptions:max-size: 5mmax-file: '1'//goto docker\restart.sh
#!/bin/bash
docker-compose down && docker system prune -f && docker-compose up -d && docker stats
//goto docker\stop.sh
#!/bin/bash
docker-compose down
//goto Dockerfile
FROM openjdk:8-jre-alpineRUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezoneCOPY target/*.jar  /app.jarEXPOSE 8080CMD ["--server.port=8080"]ENTRYPOINT ["java","-jar","/app.jar"]
//goto pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.fly</groupId><artifactId>spring-config-refresh</artifactId><version>1.0.0</version><packaging>jar</packaging><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.build.timestamp.format>yyyyMMdd-HH</maven.build.timestamp.format><docker.hub>registry.cn-shanghai.aliyuncs.com</docker.hub><java.version>1.8</java.version><skipTests>true</skipTests></properties><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.4.RELEASE</version></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId><exclusions><exclusion><groupId>org.apache.tomcat</groupId><artifactId>tomcat-jdbc</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.16</version></dependency><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>2.0.5</version></dependency><dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><scope>runtime</scope></dependency><dependency><groupId>commons-configuration</groupId><artifactId>commons-configuration</artifactId><version>1.10</version></dependency><dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-yaml</artifactId></dependency><dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-properties</artifactId></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.6</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><scope>provided</scope></dependency><!-- Test --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-configuration2</artifactId><version>2.8.0</version><scope>test</scope></dependency><dependency><groupId>commons-beanutils</groupId><artifactId>commons-beanutils</artifactId><version>1.9.4</version><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><finalName>${project.artifactId}-${project.version}</finalName><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><!-- 添加docker-maven插件 --><plugin><groupId>io.fabric8</groupId><artifactId>docker-maven-plugin</artifactId><version>0.41.0</version><executions><execution><phase>package</phase><goals><goal>build</goal><goal>push</goal><goal>remove</goal></goals></execution></executions><configuration><!-- 连接到带docker环境的linux服务器编译image --><!--<dockerHost>http://192.168.182.10:2375</dockerHost>--><!-- Docker 推送镜像仓库地址 --><pushRegistry>${docker.hub}</pushRegistry><images><image><!--推送到私有镜像仓库,镜像名需要添加仓库地址 --><name>${docker.hub}/00fly/${project.artifactId}:${project.version}-UTC-${maven.build.timestamp}</name><!--定义镜像构建行为 --><build><dockerFileDir>${project.basedir}</dockerFileDir></build></image><image><name>${docker.hub}/00fly/${project.artifactId}:${project.version}</name><build><dockerFileDir>${project.basedir}</dockerFileDir></build></image></images></configuration></plugin></plugins><resources><resource><directory>src/main/java</directory><excludes><exclude>**/*.java</exclude></excludes></resource><resource><directory>src/main/resources</directory><includes><include>**/**</include></includes></resource></resources></build>
</project>
//goto src\main\java\com\fly\BootApplication.javapackage com.fly;import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.SystemUtils;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.scheduling.annotation.EnableScheduling;import com.fly.core.utils.SpringContextUtils;import lombok.extern.slf4j.Slf4j;/*** * SpringBoot 启动入口* * @author 00fly* @version [版本号, 2018年7月20日]* @see [相关类/方法]* @since [产品/模块版本]*/
@Slf4j
@EnableScheduling
@SpringBootApplication
@PropertySource("classpath:jdbc-h2.properties")
public class BootApplication
{public static void main(String[] args){// args = new String[] {"--noweb"};boolean web = !ArrayUtils.contains(args, "--noweb");log.info("############### with Web Configuration: {} #############", web);new SpringApplicationBuilder(BootApplication.class).web(web ? WebApplicationType.SERVLET : WebApplicationType.NONE).run(args);}@Bean@ConditionalOnWebApplicationCommandLineRunner init(){return args -> {if (SystemUtils.IS_OS_WINDOWS){log.info("★★★★★★★★  now open Browser ★★★★★★★★ ");String url = SpringContextUtils.getServerBaseURL();Runtime.getRuntime().exec("cmd /c start /min " + url + "/doc.html");Runtime.getRuntime().exec("cmd /c start /min " + url + "/h2-console");}};}
}
//goto src\main\java\com\fly\core\config\Knife4jConfig.java
package com.fly.core.config;import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import io.swagger.annotations.ApiOperation;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;/*** knife4j** @author jack*/
@Configuration
@EnableSwagger2
public class Knife4jConfig
{@Value("${knife4j.enable: true}")private boolean enable;@BeanDocket api(){return new Docket(DocumentationType.SWAGGER_2).enable(enable).apiInfo(apiInfo()).groupName("Rest API").select().apis(RequestHandlerSelectors.basePackage("com.fly.refresh.web")).apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)).paths(PathSelectors.any()).build();}private ApiInfo apiInfo(){return new ApiInfoBuilder().title("接口API").description("接口文档").termsOfServiceUrl("http://00fly.online/").version("1.0.0").build();}
}
//goto src\main\java\com\fly\core\config\ScheduleThreadPoolConfig.java
package com.fly.core.config;import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;/*** * Schedule线程池配置* * @author 00fly* @version [版本号, 2023年10月22日]* @see [相关类/方法]* @since [产品/模块版本]*/
@Configuration
public class ScheduleThreadPoolConfig implements SchedulingConfigurer
{@Overridepublic void configureTasks(ScheduledTaskRegistrar taskRegistrar){ScheduledExecutorService service = new ScheduledThreadPoolExecutor(8, new CustomizableThreadFactory("schedule-pool-"));taskRegistrar.setScheduler(service);}
}
//goto src\main\java\com\fly\core\config\SysDataBaseConfig.java
package com.fly.core.config;import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;import javax.annotation.PostConstruct;import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;import lombok.Data;
import lombok.extern.slf4j.Slf4j;/*** * 数据库配置信息加载类* * @author 00fly* @version [版本号, 2021年10月24日]* @see [相关类/方法]* @since [产品/模块版本]*/
@Slf4j
@Configuration
public class SysDataBaseConfig
{@AutowiredJdbcTemplate jdbcTemplate;@AutowiredConfigurableEnvironment environment;@PostConstructpublic void initDatabasePropertySource(){// 取配置信息列表并过滤空值List<SysConfig> data = jdbcTemplate.query("SELECT `key`, `value` FROM sys_config WHERE `status` = '1'", new BeanPropertyRowMapper<>(SysConfig.class));Map<String, Object> collect = data.stream().filter(p -> StringUtils.isNoneEmpty(p.getKey(), p.getValue())).collect(Collectors.toMap(SysConfig::getKey, SysConfig::getValue));log.info("====== init from database ===== {}", collect);// 追加配置到系统变量中,name取值随意environment.getPropertySources().addLast(new MapPropertySource("sys_config", collect));}
}/*** * 配置信息实体对象* * @author 00fly* @version [版本号, 2021年10月24日]* @see [相关类/方法]* @since [产品/模块版本]*/
@Data
class SysConfig
{private String key;private String value;
}
//goto src\main\java\com\fly\core\JsonResult.java
package com.fly.core;import lombok.Data;/*** * 结果对象* * @author 00fly* @version [版本号, 2021年5月2日]* @see [相关类/方法]* @since [产品/模块版本]*/
@Data
public class JsonResult<T>
{private T data;private boolean success;private String errorCode;private String message;public JsonResult(){super();}public static <T> JsonResult<T> success(T data){JsonResult<T> r = new JsonResult<>();r.setData(data);r.setSuccess(true);return r;}public static JsonResult<?> success(){JsonResult<Object> r = new JsonResult<>();r.setSuccess(true);return r;}public static JsonResult<Object> error(String code, String msg){JsonResult<Object> r = new JsonResult<>();r.setSuccess(false);r.setErrorCode(code);r.setMessage(msg);return r;}public static JsonResult<Object> error(String msg){return error("500", msg);}
}
//goto src\main\java\com\fly\core\utils\SpringContextUtils.java
package com.fly.core.utils;import java.net.InetAddress;
import java.net.UnknownHostException;import javax.servlet.ServletContext;import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;import lombok.extern.slf4j.Slf4j;/*** Spring Context 工具类* * @author 00fly**/
@Slf4j
@Component
public class SpringContextUtils implements ApplicationContextAware
{private static ApplicationContext applicationContext;/*** web服务器基准URL*/private static String SERVER_BASE_URL = null;@Overridepublic void setApplicationContext(ApplicationContext applicationContext)throws BeansException{log.info("###### execute setApplicationContext ######");SpringContextUtils.applicationContext = applicationContext;}public static ApplicationContext getApplicationContext(){return applicationContext;}public static <T> T getBean(Class<T> clazz){Assert.notNull(applicationContext, "applicationContext is null");return applicationContext.getBean(clazz);}/*** execute @PostConstruct May be SpringContextUtils not inited, throw NullPointerException* * @return*/public static String getActiveProfile(){Assert.notNull(applicationContext, "applicationContext is null");String[] profiles = applicationContext.getEnvironment().getActiveProfiles();return StringUtils.join(profiles, ",");}/*** can use in @PostConstruct* * @param context* @return*/public static String getActiveProfile(ApplicationContext context){Assert.notNull(context, "context is null");String[] profiles = context.getEnvironment().getActiveProfiles();return StringUtils.join(profiles, ",");}/*** get web服务基准地址,一般为 http://${ip}:${port}/${contentPath}* * @return* @throws UnknownHostException* @see [类、类#方法、类#成员]*/public static String getServerBaseURL()throws UnknownHostException{if (SERVER_BASE_URL == null){ServletContext servletContext = getBean(ServletContext.class);Assert.notNull(servletContext, "servletContext is null");String ip = InetAddress.getLocalHost().getHostAddress();SERVER_BASE_URL = "http://" + ip + ":" + getProperty("server.port") + servletContext.getContextPath();}return SERVER_BASE_URL;}/*** getProperty* * @param key eg:server.port* @return* @see [类、类#方法、类#成员]*/public static String getProperty(String key){return applicationContext.getEnvironment().getProperty(key, "");}
}
//goto src\main\java\com\fly\core\utils\YamlUtils.java
package com.fly.core.utils;import java.io.IOException;
import java.util.Map;
import java.util.Properties;import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.dataformat.javaprop.JavaPropsMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;/*** * yaml转换工具* * @author 00fly* @version [版本号, 2023年4月25日]* @see [相关类/方法]* @since [产品/模块版本]*/
public final class YamlUtils
{private static YAMLMapper yamlMapper = new YAMLMapper();private static JavaPropsMapper javaPropsMapper = new JavaPropsMapper();/*** yaml转Json字符串* * @param yamlContent* @return* @throws IOException*/public static String yamlToJson(String yamlContent)throws IOException{JsonNode jsonNode = yamlMapper.readTree(yamlContent);return jsonNode.toPrettyString();}/*** yaml转Map<String, String>* * @param yamlContent* @return* @throws IOException*/public static Map<String, String> yamlToMap(String yamlContent)throws IOException{JsonNode jsonNode = yamlMapper.readTree(yamlContent);return javaPropsMapper.writeValueAsMap(jsonNode);}/*** yaml转properties* * @param yamlContent* @return* @throws IOException*/public static Properties yamlToProperties(String yamlContent)throws IOException{JsonNode jsonNode = yamlMapper.readTree(yamlContent);return javaPropsMapper.writeValueAsProperties(jsonNode);}/*** yaml转properties字符串* * @param yamlContent* @return* @throws IOException*/public static String yamlToPropText(String yamlContent)throws IOException{JsonNode jsonNode = yamlMapper.readTree(yamlContent);return javaPropsMapper.writeValueAsString(jsonNode);}private YamlUtils(){super();}
}
//goto src\main\java\com\fly\refresh\back\ReloadByDataBase.java
package com.fly.refresh.back;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;import com.fly.refresh.entity.Welcome;import lombok.extern.slf4j.Slf4j;/*** 数据库配置表手动刷新*/
@Slf4j
@Service
public class ReloadByDataBase
{@AutowiredWelcome welcome;@AutowiredJdbcTemplate jdbcTemplate;/*** 更新到数据库* * @param message* @return*/public int update(String message){int count = jdbcTemplate.update("UPDATE sys_config SET `value`=? WHERE `key` = 'welcome.message'", message);if (count > 0){log.info("#### autoRefresh to: {}", message);welcome.setMessage(message);}return count;}
}
//goto src\main\java\com\fly\refresh\back\ReloadByFileAlterationMonitor.java
package com.fly.refresh.back;import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Properties;
import java.util.concurrent.TimeUnit;import javax.annotation.PostConstruct;import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ResourceUtils;import com.fasterxml.jackson.dataformat.javaprop.JavaPropsMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
import com.fly.refresh.entity.Person;
import com.fly.refresh.entity.Result;
import com.fly.refresh.entity.Welcome;import lombok.extern.slf4j.Slf4j;/*** 监听文件变化(推荐)*/
@Slf4j
@Component
public class ReloadByFileAlterationMonitor
{@Autowiredprivate Person person;@Autowiredprivate Welcome welcome;/*** thread-safe*/YAMLMapper yamlMapper = new YAMLMapper();/*** thread-safe*/JavaPropsMapper javaPropsMapper = new JavaPropsMapper();/*** 初始化yml文件监听器*/@PostConstructpublic void initYamlMonitor(){try{URL url = ResourceUtils.getURL(ResourceUtils.CLASSPATH_URL_PREFIX);if (ResourceUtils.isFileURL(url)){FileAlterationObserver observer = new FileAlterationObserver(url.getPath(), FileFilterUtils.suffixFileFilter(".yml"));observer.addListener(new FileAlterationListenerAdaptor(){@Overridepublic void onFileChange(File file){log.info("★★★★★★★★ {} changed.", file.getName());if (StringUtils.equals("application-dev.yml", file.getName())){try{// yaml to JavaBeanString text = FileUtils.readFileToString(file, StandardCharsets.UTF_8);Result javaBean = yamlMapper.readValue(text, Result.class);// Welcome属性拷贝if (javaBean != null && javaBean.getWelcome() != null){Welcome from = javaBean.getWelcome();BeanUtils.copyProperties(from, welcome);log.info("#### autoRefresh to: {}", welcome);}// Person属性拷贝if (javaBean != null && javaBean.getPerson() != null){Person from = javaBean.getPerson();BeanUtils.copyProperties(from, person);log.info("#### autoRefresh to: {}", person);}}catch (IOException e){log.error(e.getMessage(), e.getCause());}}}});long interval = TimeUnit.SECONDS.toMillis(10);FileAlterationMonitor monitor = new FileAlterationMonitor(interval, observer);monitor.start();}}catch (Exception e){log.error(e.getMessage(), e.getCause());}}/*** 初始化Properties文件监听器*/@PostConstructpublic void initPropsMonitor(){try{URL url = ResourceUtils.getURL(ResourceUtils.CLASSPATH_URL_PREFIX);if (ResourceUtils.isFileURL(url)){FileAlterationObserver observer = new FileAlterationObserver(url.getPath(), FileFilterUtils.suffixFileFilter(".properties"));observer.addListener(new FileAlterationListenerAdaptor(){@Overridepublic void onFileChange(File file){log.info("★★★★★★★★ {} changed.", file.getName());if (StringUtils.equals("welcome.properties", file.getName())){try{// Properties to JavaBeanProperties prop = PropertiesLoaderUtils.loadProperties(new ClassPathResource(file.getName()));Result javaBean = javaPropsMapper.readPropertiesAs(prop, Result.class);if (javaBean != null && javaBean.getWelcome() != null){String value = javaBean.getWelcome().getMessage();log.info("#### autoRefresh to: {}", value);welcome.setMessage(value);}}catch (IOException e){log.error(e.getMessage(), e.getCause());}}}});long interval = TimeUnit.SECONDS.toMillis(10);FileAlterationMonitor monitor = new FileAlterationMonitor(interval, observer);monitor.start();}}catch (Exception e){log.error(e.getMessage(), e.getCause());}}}
//goto src\main\java\com\fly\refresh\back\ReloadByReloadingStrategy.java
package com.fly.refresh.back;import javax.annotation.PostConstruct;import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.FileConfiguration;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;import com.fly.refresh.entity.Welcome;import lombok.extern.slf4j.Slf4j;/*** 文件重加载策略(不推荐)*/
@Slf4j
@Component
public class ReloadByReloadingStrategy
{String lastMsg;@AutowiredWelcome welcome;FileConfiguration propConfig;/*** 初始化properties文件重加载策略*/@PostConstructpublic void initReloadingStrategy(){try{// 只支持propertiespropConfig = new PropertiesConfiguration("welcome.properties");FileChangedReloadingStrategy strategy = new FileChangedReloadingStrategy();strategy.setRefreshDelay(10000L);propConfig.setReloadingStrategy(strategy);lastMsg = propConfig.getString("welcome.message");}catch (ConfigurationException e){log.error(e.getMessage(), e.getCause());}}/*** 配置变更时刷新*/@Scheduled(initialDelay = 30000L, fixedRate = 10000L)public void autoRefresh(){// 是否变更,何时刷新逻辑实现String message = propConfig.getString("welcome.message");if (!StringUtils.equals(message, lastMsg)){log.info("#### autoRefresh to: {}, after properties Changed", message);welcome.setMessage(message);lastMsg = message;}}
}
//goto src\main\java\com\fly\refresh\entity\Dog.java
package com.fly.refresh.entity;import lombok.Data;@Data
public class Dog
{private String name;private Integer age;
}
//goto src\main\java\com\fly\refresh\entity\Person.java
package com.fly.refresh.entity;import java.util.Date;
import java.util.List;
import java.util.Map;import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Component;import lombok.Data;@Data
@Component
@ConfigurationProperties(prefix = "person")
public class Person
{private String name;private Integer age;private Boolean happy;@DateTimeFormat(pattern = "yyyy-MM-dd")private Date birth;private Map<String, Object> maps;private List<Object> lists;private Dog dog;
}
//goto src\main\java\com\fly\refresh\entity\Result.java
package com.fly.refresh.entity;import java.util.Map;import lombok.Data;/*** 定义Result实体绑定Person、Welcome<br>* 与下面的Spring配置等价<br>* <br>* @Component<br>* @ConfigurationProperties(prefix = "person")<br>* public class Person { ... }<br>* <br>* @Component<br>* @ConfigurationProperties(prefix = "welcome")<br>* public class Welcome { ... }*/
@Data
public class Result
{private Person person;private Welcome welcome;private Map<String, Object> spring;
}
//goto src\main\java\com\fly\refresh\entity\Welcome.java
package com.fly.refresh.entity;import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;import lombok.Data;/*** * Welcome配置文件实体<br>* 使用@Lazy待SysDataBaseConfig方法initDatabasePropertySource执行完再注入<br>* 否则仅使用数据库初始化时开发环境和Jar运行message值不一致* * @author 00fly* @version [版本号, 2023年11月3日]* @see [相关类/方法]* @since [产品/模块版本]*/
@Data
@Lazy
@Component
@ConfigurationProperties(prefix = "welcome")
public class Welcome
{/*** message赋值方式:<br>* 1. Configuration注解在SysDataBaseConfig<br>* 2. spring.profiles.active指定dev即application-dev.yml<br>* 3. welcome.properties内容变更时触发<br>* 4. /show/refresh接口被调用时触发<br>* 方式1、2有竞争,不能严格区分先后*/private String message = "hello, 00fly in java!";
}
//goto src\main\java\com\fly\refresh\job\SimpleJob.java
package com.fly.refresh.job;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;import com.fly.refresh.entity.Person;
import com.fly.refresh.entity.Welcome;import lombok.extern.slf4j.Slf4j;/*** * SimpleJob* * @author 00fly* @version [版本号, 2022年11月30日]* @see [相关类/方法]* @since [产品/模块版本]*/
@Slf4j
@Component
public class SimpleJob
{@Autowiredprivate Person person;@Autowiredprivate Welcome welcome;/*** 不能实时刷新*/@Value("#{welcome.message}")private String message;@Scheduled(cron = "*/10 * * * * ?")public void run(){log.info("---- autoRefresh: {} | fixed: {}", welcome.getMessage(), message);log.info("**** {}, {}", welcome, person);}
}
//goto src\main\java\com\fly\refresh\ResourceReloadConfig.java
package com.fly.refresh;import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.ResourceBundle;
import java.util.concurrent.ScheduledThreadPoolExecutor;import javax.annotation.PostConstruct;import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.math.RandomUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnNotWebApplication;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;import lombok.extern.slf4j.Slf4j;/*** 配置文件实时刷新* * @author 00fly* @version [版本号, 2017年4月25日]* @see [相关类/方法]* @since [产品/模块版本]*/
@Slf4j
@Component
@ConditionalOnNotWebApplication
public class ResourceReloadConfig implements SchedulingConfigurer
{PropertiesConfiguration jobConfig;Resource cron = new ClassPathResource("test/cron.properties");ResourceBundle job = ResourceBundle.getBundle("test/job");@Overridepublic void configureTasks(ScheduledTaskRegistrar taskRegistrar){// 配置公共Schedule线程池taskRegistrar.setScheduler(new ScheduledThreadPoolExecutor(8, new CustomizableThreadFactory("schedule-pool-")));// 配置TriggerTasktaskRegistrar.addTriggerTask(new Runnable(){@Overridepublic void run(){// 任务逻辑log.info("★★★★★★★ {} run ★★★★★★★", getClass().getName());}}, new Trigger(){@Overridepublic Date nextExecutionTime(TriggerContext triggerContext){String cron = readCronText();return new CronTrigger(cron).nextExecutionTime(triggerContext);}});}/*** 初始化*/@PostConstructpublic void init(){try{jobConfig = new PropertiesConfiguration("test/job.properties");FileChangedReloadingStrategy strategy = new FileChangedReloadingStrategy();strategy.setRefreshDelay(60000L);// 刷新周期1分钟jobConfig.setReloadingStrategy(strategy);}catch (ConfigurationException e){log.error(e.getMessage(), e.getCause());}}/*** 3种方式读取CronText* * @return*/private String readCronText(){String cronText = "*/10 * * * * ?";Integer key = RandomUtils.nextInt(3);switch (key){case 0:cronText = jobConfig.getString("schedule.myjob.cron");break;case 1:try{cronText = IOUtils.toString(cron.getURL(), StandardCharsets.UTF_8);}catch (IOException e){log.error(e.getMessage(), e.getCause());}break;case 2:ResourceBundle.clearCache();cronText = job.getString("schedule.myjob.cron");break;default:break;}log.info("**** key: {} ==> {}", key, cronText);return cronText;}
}
//goto src\main\java\com\fly\refresh\web\ShowController.java
package com.fly.refresh.web;import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import com.fly.core.JsonResult;
import com.fly.refresh.back.ReloadByDataBase;import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;@RestController
@Api(tags = "演示接口")
@RequestMapping("/show")
public class ShowController
{@AutowiredReloadByDataBase reloadByDataBase;@ApiOperation("刷新欢迎语")@PostMapping("/refresh")@ApiImplicitParam(name = "message", value = "欢迎语", example = "热烈欢迎活捉洪真英,生擒李知恩! ", required = true)public JsonResult<?> refresh(String message){if (StringUtils.isBlank(message)){return JsonResult.error("message不能为空");}boolean success = reloadByDataBase.update(message) > 0;return success ? JsonResult.success(message) : JsonResult.error("刷新欢迎语失败");}
}
//goto src\main\resources\application-dev.yml
person: name: qinjiangage: 18happy: falsebirth: 2000-01-01maps: {k1: v1,k2: v2}lists:- code- girl- musicdog:name: 旺财age: 1welcome:message: Hello 00fly in application-dev.yml
//goto src\main\resources\application-prod.yml
//goto src\main\resources\application-test.yml
//goto src\main\resources\application.yml
server:port: 8080servlet:context-path: /session:timeout: 1800
spring:datasource:url: ${druid.url}username: ${druid.username}password: ${druid.password}driverClassName: ${druid.driverClassName}type: com.alibaba.druid.pool.DruidDataSourcesqlScriptEncoding: utf-8schema: classpath:sql/schema.sqlcontinue-on-error: truedruid:initial-size: 5                                       # 初始化大小min-idle: 10                                          # 最小连接数max-active: 20                                        # 最大连接数max-wait: 60000                                       # 获取连接时的最大等待时间min-evictable-idle-time-millis: 300000                # 一个连接在池中最小生存的时间,单位是毫秒time-between-eviction-runs-millis: 60000              # 多久才进行一次检测需要关闭的空闲连接,单位是毫秒validation-query: SELECT 1                            # 检测连接是否有效的 SQL语句,为空时以下三个配置均无效test-on-borrow: true                                  # 申请连接时执行validationQuery检测连接是否有效,默认true,开启后会降低性能test-on-return: true                                  # 归还连接时执行validationQuery检测连接是否有效,默认false,开启后会降低性能test-while-idle: true                                 # 申请连接时如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效,默认false,建议开启,不影响性能devtools:restart:exclude: application-dev.yml,welcome.propertiesh2:console:enabled: truepath: /h2-consolesettings:web-allow-others: trueprofiles:active:- dev
//goto src\main\resources\jdbc-h2.properties
druid.username=sa
druid.password=
druid.url=jdbc:h2:mem:reload;database_to_upper=false
druid.driverClassName=org.h2.Driver
//goto src\main\resources\sql\schema.sql
CREATE TABLE IF NOT EXISTS `sys_config` (`id` bigint NOT NULL AUTO_INCREMENT,`key` varchar(100),`value` varchar(200),`description` varchar(200),`status` varchar(20),`version` bigint,`creater` varchar(50),`create_time` datetime,`modifier` varchar(50),`modify_time` datetime,PRIMARY KEY (`id`)
);INSERT INTO `sys_config` VALUES ('1', 'welcome.message',  CONCAT('hello from db, rand ' ,CAST(RAND()*65536 AS INT)), '系统提示语', '1', '0', 'admin', now(), 'admin', now());
//goto src\main\resources\test\cron.properties
*/5 * * * * ?
//goto src\main\resources\test\job.properties
schedule.myjob.cron = */5 * * * * ?
//goto src\main\resources\welcome.properties
welcome.message = Hello 00fly in welcome.properties
//goto src\test\java\com\fly\refresh\config2\ResourceReloadConfigTest.java
package com.fly.refresh.config2;import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;import org.apache.commons.configuration2.PropertiesConfiguration;
import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
import org.apache.commons.configuration2.builder.ReloadingFileBasedConfigurationBuilder;
import org.apache.commons.configuration2.builder.fluent.Configurations;
import org.apache.commons.configuration2.builder.fluent.Parameters;
import org.apache.commons.configuration2.builder.fluent.PropertiesBuilderParameters;
import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler;
import org.apache.commons.configuration2.ex.ConfigurationException;
import org.apache.commons.configuration2.io.ClasspathLocationStrategy;
import org.apache.commons.configuration2.io.FileLocationStrategy;
import org.apache.commons.configuration2.reloading.PeriodicReloadingTrigger;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;import lombok.extern.slf4j.Slf4j;/*** Configuration2配置文件实时刷新 https://www.geek-share.com/detail/2727072209.html* * @author 00fly* @version [版本号, 2017年4月25日]* @see [相关类/方法]* @since [产品/模块版本]*/
@Slf4j
public class ResourceReloadConfigTest
{ReloadingFileBasedConfigurationBuilder<PropertiesConfiguration> builder;/*** 初始化*/@Beforepublic void init(){// 文件扫描策略// FileLocationStrategy strategy = new CombinedLocationStrategy(Arrays.asList(new ClasspathLocationStrategy(), new FileSystemLocationStrategy()));FileLocationStrategy strategy = new ClasspathLocationStrategy();PropertiesBuilderParameters propertiesBuilderParameters = new Parameters().properties().setEncoding(StandardCharsets.UTF_8.name()).setPath(new ClassPathResource("job.properties").getPath()).setLocationStrategy(strategy).setListDelimiterHandler(new DefaultListDelimiterHandler(',')).setReloadingRefreshDelay(2000L).setThrowExceptionOnMissing(true);builder = new ReloadingFileBasedConfigurationBuilder<PropertiesConfiguration>(PropertiesConfiguration.class).configure(propertiesBuilderParameters);PeriodicReloadingTrigger trigger = new PeriodicReloadingTrigger(builder.getReloadingController(), null, 60, TimeUnit.SECONDS);trigger.start();}@Testpublic void read()throws ConfigurationException{// 直接读取Configurations configs = new Configurations();FileBasedConfigurationBuilder.setDefaultEncoding(PropertiesConfiguration.class, StandardCharsets.UTF_8.name());PropertiesConfiguration propConfig = configs.properties(new ClassPathResource("job.properties").getPath());log.info("propConfig:{}", propConfig.getString("schedule.myjob.cron"));}/*** https://cloud.tencent.com/developer/article/1600688* * @throws ConfigurationException*/@Testpublic void test()throws ConfigurationException{PropertiesConfiguration configuration = builder.getConfiguration();log.info("{}", configuration.getString("schedule.myjob.cron"));}
}
//goto src\test\java\com\fly\refresh\prop\JavaPropsMapperTest.java
package com.fly.refresh.prop;import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Properties;import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;import com.fasterxml.jackson.dataformat.javaprop.JavaPropsMapper;
import com.fly.core.utils.YamlUtils;
import com.fly.refresh.entity.Result;import lombok.extern.slf4j.Slf4j;@Slf4j
public class JavaPropsMapperTest
{/*** thread-safe*/JavaPropsMapper javaPropsMapper = new JavaPropsMapper();/*** Properties to Bean* * @throws IOException*/@Testpublic void testPropToBean()throws IOException{Properties complex = PropertiesLoaderUtils.loadProperties(new ClassPathResource("prop/complex.properties"));// 多层结构转换成了嵌套MapMap<?, ?> map = javaPropsMapper.readPropertiesAs(complex, Map.class);log.info("***** PropToBean:{} => {}", complex, map);Result javaBean = javaPropsMapper.readPropertiesAs(complex, Result.class);log.info("***** PropToBean:{} => {}", complex, javaBean);}/*** Properties to Bean* * @throws IOException*/@Testpublic void testPropToBean2()throws IOException{String text = IOUtils.toString(new ClassPathResource("yaml/sample.yml").getURL(), StandardCharsets.UTF_8);Properties props = YamlUtils.yamlToProperties(text);props.keySet().forEach(key -> {log.info("{} => {}", key, props.get(key));});Result result = javaPropsMapper.readPropertiesAs(props, Result.class);log.info("***** PropToBean:{}", result);}
}
//goto src\test\java\com\fly\refresh\yaml\SampleTest.java
package com.fly.refresh.yaml;import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Properties;import org.apache.commons.io.IOUtils;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import org.yaml.snakeyaml.Yaml;import com.fasterxml.jackson.dataformat.javaprop.JavaPropsMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
import com.fly.core.utils.YamlUtils;
import com.fly.refresh.entity.Result;import lombok.extern.slf4j.Slf4j;@Slf4j
public class SampleTest
{static String yamlText;/*** thread-safe*/YAMLMapper yamlMapper = new YAMLMapper();@BeforeClasspublic static void init(){try{yamlText = IOUtils.toString(new ClassPathResource("yaml/sample.yml").getURL(), StandardCharsets.UTF_8);log.info("yamlText => {}", yamlText);}catch (IOException e){log.error(e.getMessage(), e.getCause());}}/*** 解析带prefix的yaml*/@Testpublic void test()throws IOException{Result result = new Yaml().loadAs(yamlText, Result.class);log.info("snakeyaml  toJavaBean: {}", result);result = yamlMapper.readValue(yamlText, Result.class);log.info("yamlMapper toJavaBean: {}", result);}@Testpublic void test2()throws IOException{// TODO: yamlText截取person内容转换为Person对象Properties props = YamlUtils.yamlToProperties(yamlText);log.info("Properties: {}", props);Result result = new JavaPropsMapper().readPropertiesAs(props, Result.class);log.info("***** PropToBean:{}", result);}
}
//goto src\test\java\com\fly\refresh\yaml\SnakeYamlTest.java
package com.fly.refresh.yaml;import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Properties;import org.apache.commons.io.IOUtils;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.yaml.snakeyaml.Yaml;import com.fly.core.utils.YamlUtils;
import com.fly.refresh.entity.Result;import lombok.extern.slf4j.Slf4j;@Slf4j
public class SnakeYamlTest
{private static String text;@BeforeClasspublic static void init(){try{Resource resource = new ClassPathResource("yaml/complex.yml");text = IOUtils.toString(resource.getURL(), StandardCharsets.UTF_8);log.info("yamlText => {}", text);}catch (IOException e){log.error(e.getMessage(), e.getCause());}}@Testpublic void test(){Yaml yaml = new Yaml();Result javaBean = yaml.loadAs(text, Result.class);log.info("***** toJavaBean => {}", javaBean);}/*** 注意区别*/@Testpublic void testPk()throws IOException{Yaml yaml = new Yaml();Properties prop1 = yaml.loadAs(text, Properties.class);Properties prop2 = YamlUtils.yamlToProperties(text);log.info("** PK ** {} <=> {}", prop1, prop2);// {welcome={message=Hello 00fly in test2.yml}} <=> {welcome.message=Hello 00fly in test2.yml}}
}
//goto src\test\java\com\fly\refresh\yaml\YAMLMapperTest.java
package com.fly.refresh.yaml;import java.io.IOException;
import java.nio.charset.StandardCharsets;import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
import com.fly.refresh.entity.Result;import lombok.extern.slf4j.Slf4j;@Slf4j
public class YAMLMapperTest
{/*** thread-safe*/YAMLMapper yamlMapper = new YAMLMapper();@Testpublic void test()throws IOException{Resource resource = new ClassPathResource("yaml/complex.yml");String text = IOUtils.toString(resource.getURL(), StandardCharsets.UTF_8);log.info("***** complex.yml yamlText => {}", text);Result javaBean = yamlMapper.readValue(text, Result.class);log.info("***** toJavaBean => {}", javaBean);// 报错com.fasterxml.jackson.databind.exc.MismatchedInputException// Properties prop = yamlMapper.readValue(text, Properties.class);// log.info("***** toJavaBean => {}", prop);}
}
//goto src\test\resources\job.properties
schedule.myjob.cron = */5 * * * * ?
//goto src\test\resources\log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="off" monitorInterval="0"><appenders><console name="Console" target="system_out"><patternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %c - %msg%n" /></console></appenders><loggers><root level="INFO"><appender-ref ref="Console" /></root></loggers>
</configuration>
//goto src\test\resources\prop\complex.properties
welcome.message=Hello 00fly in complex
//goto src\test\resources\yaml\complex.yml
welcome:message: Hello 00fly in test2.yml
//goto src\test\resources\yaml\sample.yml
spring:datasource:url: ${druid.url}username: ${druid.username}password: ${druid.password}driverClassName: ${druid.driverClassName}type: com.alibaba.druid.pool.DruidDataSourcesqlScriptEncoding: utf-8schema: classpath:sql/schema.sqlcontinue-on-error: truedruid:initial-size: 5                                       # 初始化大小min-idle: 10                                          # 最小连接数max-active: 20                                        # 最大连接数max-wait: 60000                                       # 获取连接时的最大等待时间min-evictable-idle-time-millis: 300000                # 一个连接在池中最小生存的时间,单位是毫秒time-between-eviction-runs-millis: 60000              # 多久才进行一次检测需要关闭的空闲连接,单位是毫秒validation-query: SELECT 1                            # 检测连接是否有效的 SQL语句,为空时以下三个配置均无效test-on-borrow: true                                  # 申请连接时执行validationQuery检测连接是否有效,默认true,开启后会降低性能test-on-return: true                                  # 归还连接时执行validationQuery检测连接是否有效,默认false,开启后会降低性能test-while-idle: true                                 # 申请连接时如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效,默认false,建议开启,不影响性能devtools:restart:exclude: application-dev.yml,welcome.propertiesperson: name: qinjiangage: 18happy: falsebirth: 2000-01-01maps: {k1: v1,k2: v2}lists:- code- girl- musicdog:name: 旺财age: 1

3. 运行说明

  1. 系统启动后从内存数据库h2表sys_config加载配置,从application-dev.yml加载配置
  2. 修改application-dev.yml、welcome.properties可以看到配置被动态刷新
  3. /show/refresh 接口调用刷新配置
  4. 如需要测试数据库配置,请在application.yml设置spring.profiles.active为test或者prod

有任何问题和建议,都可以向我提问讨论,大家一起进步,谢谢!

-over-

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/137510.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大容量疯了!居然想把磁带放到硬盘,100TB+是否可以实现?

1.引言 上一篇关于大容量硬盘的文章&#xff08;HDD最后的冲刺&#xff1a;大容量硬盘的奋力一搏&#xff09;中&#xff0c;我们针对大容量硬盘研发状态&#xff0c;小编最近又有了新发现。WDC希望可以通过HDD和磁带结合&#xff0c;把盘的容量提升到100TB。 2.数据大爆炸的…

问题描述:64位计算机的寻址能力是多少TB

问题描述&#xff1a;64位计算机的寻址能力是多少TB 我在看到一个32位电脑的寻址能力计算时&#xff0c;看到是这么计算的。 虚拟内存的大小受到计算机地址位数的限制&#xff0c; 那么32位电脑的寻址能力计算应该是这样 为什么网上百度到的是16TB呢&#xff0c;如下图所示 中…

大数据毕业设计选题推荐-农作物观测站综合监控平台-Hadoop-Spark-Hive

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

Web实验总

目录 网站需求&#xff1a; 思路&#xff1a; 实验步骤&#xff1a; 第一步&#xff1a;准备工作 第二步&#xff1a;新建一个存储网页的目录 第三步&#xff1a;修改本地hosts映射 第四步&#xff1a;修改配置文件&#xff0c;建立基于http服务的网站 1)创建用户song和…

宠物医院信息展示预约小程序的效果如何

养宠家庭越来越多&#xff0c;随之带来的就是宠物健康问题&#xff0c;生活条件稍微好点的家庭&#xff0c;只要宠物生病或洗护、寄养、美容等就会前往宠物医院&#xff0c;而近些年来&#xff0c;市场中的宠物医院也在连年增加&#xff0c;可以预见市场需求度较高。 而对宠物…

打开ps提示,计算机中丢失d3dcompiler_47.dll怎么解决?

“d3dcompiler_47.dll丢失5个解决办法”。相信很多同事在工作或者娱乐的过程中&#xff0c;都遇到过这个错误提示。那么&#xff0c;究竟什么是d3dcompiler_47.dll文件&#xff1f;为什么会丢失呢&#xff1f;又该如何解决这个问题呢&#xff1f;接下来&#xff0c;我将为大家详…

【CMU 15-445】Proj1 Buffer Pool Manager

Buffer Pool Manager 通关记录Task1 LRU-K Replacement PolicyTask2 Disk SchedulerTask3 Buffer Pool ManagerFlushPageFlushAllPagesUnpinPageNewPageFetchPageDeletePage Optimizations CMU-15445汇总 本文对应的project版本为CMU-Fall-2023的project1 由于Andy要求&#xf…

JavaEE初阶学习:Linux 基本使用和 web 程序部署

1.Linux的基本认识 Linux 是一个操作系统.(搞管理的系统) 和Windows都是同类产品~~ Linux 实际的场景: 1.服务器 2.嵌入式设备 3.移动端(手机)Android 其实就是Linux 1991年,还在读大学的 芬兰人 Linus Benedict Torvalds,搞了一个Linux 这样的系统0.01版,正式发布了~ 后…

基于pytorch使用特征图输出进行特征图可视化

使用特征图输出进行特征图可视化 文章目录 前言效果展示获取某一层特征图输出原图方法一&#xff1a;使用IntermediateLayerGetter类方法二&#xff1a;使用hook机制&#xff08;推荐&#xff09; 总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 例…

【 云原生 | K8S 】kubectl 详解

目录 1 kubectl 2 基本信息查看 2.1 查看 master 节点状态 2.2 查看命名空间 2.3 查看default命名空间的所有资源 2.4 创建命名空间app 2.5 删除命名空间app 2.6 在命名空间kube-public 创建副本控制器&#xff08;deployment&#xff09;来启动Pod&#xff08;nginx-wl…

大数据-之LibrA数据库系统告警处理(ALM-12036 license文件即将过期)

告警解释 系统每天零点检查一次当前系统中的license文件&#xff0c;如果当前时间距离过期时间不足60天&#xff0c;则license文件即将过期&#xff0c;产生该告警。 当重新导入一个正常license&#xff0c;告警恢复。 说明&#xff1a; 如果当前集群使用节点数小于等于10节…

RLHF的替代算法之DPO原理解析:从Zephyr的DPO到Claude的RAILF

前言 本文的成就是一个点顺着一个点而来的&#xff0c;成文过程颇有意思 首先&#xff0c;如上文所说&#xff0c;我司正在做三大LLM项目&#xff0c;其中一个是论文审稿GPT第二版&#xff0c;在模型选型的时候&#xff0c;关注到了Mistral 7B(其背后的公司Mistral AI号称欧洲…

049-第三代软件开发-软件部署脚本(一)

第三代软件开发-软件部署脚本(一) 文章目录 第三代软件开发-软件部署脚本(一)项目介绍软件部署脚本(一)其他方式 关键字&#xff1a; Qt、 Qml、 bash、 shell、 脚本 项目介绍 欢迎来到我们的 QML & C 项目&#xff01;这个项目结合了 QML&#xff08;Qt Meta-Object…

nfs配置

1.NFS介绍 NFS就是Network File System的缩写&#xff0c;它最大的功能就是可以通过网络&#xff0c;让不同的机器、不同的操 作系统可以共享彼此的文件。 NFS服务器可以让PC将网络中的NFS服务器共享的目录挂载到本地端的文 件系统中&#xff0c;而在本地端的系统中来看&#…

【Git】Gui图形化管理、SSH协议私库集成IDEA使用

一、Gui图形化界面使用 1、根据自己需求打开管理器 2、克隆现有的库 3、图形化界面介绍 1、首先在本地仓库更新一个代码文件&#xff0c;进行使用&#xff1a; 2、进入图形管理界面刷新代码资源&#xff1a; 3、点击Stage changed 跟踪文件&#xff0c;将文件处于暂存区 4、通过…

详解机器学习最优化算法

前言 对于几乎所有机器学习算法&#xff0c;无论是有监督学习、无监督学习&#xff0c;还是强化学习&#xff0c;最后一般都归结为求解最优化问题。因此&#xff0c;最优化方法在机器学习算法的推导与实现中占据中心地位。在这篇文章中&#xff0c;小编将对机器学习中所使用的…

算法之路(一)

&#x1f58a;作者 : D. Star. &#x1f4d8;专栏 :算法小能手 &#x1f606;今日分享 : 如何学习&#xff1f; 在学习的过程中&#xff0c;不仅要知道如何学习&#xff0c;还要知道避免学习的陷阱。1. 睡眠不足&#xff1b;2. 被动学习和重读&#xff1b;3. 强调标记或画线&am…

使用Ruby编写通用爬虫程序

目录 一、引言 二、环境准备 三、爬虫程序设计 1. 抓取网页内容 2. 解析HTML内容 3. 提取特定信息 4. 数据存储 四、优化和扩展 五、结语 一、引言 网络爬虫是一种自动抓取互联网信息的程序。它们按照一定的规则和算法&#xff0c;遍历网页并提取所需的信息。使用Rub…

初识Linux:目录路径

目录 提示&#xff1a;以下指令均在Xshell 7 中进行 一、基本指令&#xff1a; 二、文件 文件内容文件属性 三、ls 指令拓展 1、 ls -l &#xff1a; 2、ls -la&#xff1a; 3、ls [目录名] &#xff1a; 4、ls -ld [目录名]&#xff1a; 四、Linux中的文件和…

串口通信(11)-CRC校验介绍算法

本文为博主 日月同辉&#xff0c;与我共生&#xff0c;csdn原创首发。希望看完后能对你有所帮助&#xff0c;不足之处请指正&#xff01;一起交流学习&#xff0c;共同进步&#xff01; > 发布人&#xff1a;日月同辉,与我共生_单片机-CSDN博客 > 欢迎你为独创博主日月同…