springboot配置项动态刷新

news/2024/11/15 8:41:40/

文章目录

  • 一,序言
  • 二,准备工作
    • 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.ppmy.cn/news/1328402.html

相关文章

C++设计模式(李建忠)笔记4(完结)

C设计模式&#xff08;李建忠&#xff09; 本文是学习笔记&#xff0c;如有侵权&#xff0c;请联系删除。 参考链接 Youtube: C设计模式 Gtihub源码与PPT&#xff1a;https://github.com/ZachL1/Bilibili-plus 豆瓣: 设计模式–可复用面向对象软件的基础 总结23种设计模式…

清华大学计算机学科推荐学术会议和期刊列表——网络与信息安全

A类会议 这里仅列出A类会议 会议简称会议全程网址IEEE S&PIEEE Symposium on Security and Privacyhttp://dblp.uni-trier.de/db/conf/sp/NDSSISOC Network and Distributed System Security Symposiumhttp://dblp.uni-trier.de/db/conf/ndss/USENIX SecurityUsenix Secu…

1d 卷积网络笔记

目录 这个是1d 卷积网络合集&#xff1a; resnet1d的 这个是1d 卷积网络合集&#xff1a; https://github.com/StChenHaoGitHub/1D-deeplearning-model-pytorch/blob/main/ResNet50.py resnet1d的 https://github.com/hsd1503/resnet1d

php基础学习之常量

php常量的基本概念 常量是在程序运行中的一种不可改变的量&#xff08;数据&#xff09;&#xff0c;常量一旦定义&#xff0c;通常不可改变&#xff08;用户级别&#xff09;。 php常量的定义形式 使用define函数&#xff1a;define("常量名字", 常量值);使用cons…

【设计模式】代理模式的实现方式与使用场景

1. 概述 代理模式是一种结构型设计模式&#xff0c;它通过创建一个代理对象来控制对另一个对象的访问&#xff0c;代理对象在客户端和目标对象之间充当了中介的角色&#xff0c;客户端不再直接访问目标对象&#xff0c;而是通过代理对象间接访问目标对象。 那在中间加一层代理…

数据库安全-第一章 Mysql 安全基础-【web 环境搭建——LAMP-1】-LAMP LNMP 简介

文章目录 相关知识WEB 应用程序运作方式简介什么是 LAMP题目什么是 LNMP题目什么是 WNMP题目补充&#xff1a;HTTP 协议题目 相关知识 WEB 应用程序运作方式简介 WEB 的本意是蜘蛛网和网&#xff0c;在网页设计中称为网页。现广泛译作网络、互联网等技术领域。日常生活中用户…

《WebKit 技术内幕》之六(1): CSS解释器和样式布局

《WebKit 技术内幕》之六&#xff08;1&#xff09;&#xff1a;CSS解释器和样式布局 CSS解释器和规则匹配处于DOM树建立之后&#xff0c;RenderObject树之前&#xff0c;CSS解释器解释后的结果会保存起来&#xff0c;然后RenderObject树基于该结果来进行规范匹配和布局计算。当…

项目整合管理-8.3指导与管理项目工作

指导与管理项目工作是为实现项目目标而领导和执行项目管理计划中所确定的工作&#xff0c;并实施己批准变更的过程。 本过程的主要作用是对项目工作和可交付成果开展综合管理&#xff0c;以提高项目成功的可能性.。本过程需要在整个项目期间开展。指导与管理项目工作过程的数据…