springboot2 中怎么动态加载properties 文件
发表于:2025-02-02 作者:千家信息网编辑
千家信息网最后更新 2025年02月02日,这期内容当中小编将会给大家带来有关springboot2 中怎么动态加载properties 文件,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。1、比较好的方案,采
千家信息网最后更新 2025年02月02日springboot2 中怎么动态加载properties 文件
这期内容当中小编将会给大家带来有关springboot2 中怎么动态加载properties 文件,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。
1、比较好的方案,采用文件监控 依赖 commons-io2
commons-io commons-io 2.5
2、编写监听器
import java.io.File;import com.dingxianginc.channelaggregation.webconfig.properties.PropertyConfig;import lombok.extern.slf4j.Slf4j;import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;import org.apache.commons.io.monitor.FileAlterationObserver;import org.springframework.beans.BeansException;import org.springframework.stereotype.Component;/** * 自定义文件监听器 * @author ysma */@Slf4j@Componentpublic class FileListener extends FileAlterationListenerAdaptor{ private PropertyConfig propertyConfig; private String configDir; public FileListener() { } public FileListener(PropertyConfig propertyConfig, String configDir) { this.propertyConfig = propertyConfig; this.configDir = configDir; } @Override public void onStart(FileAlterationObserver observer) { log.debug("FileListener 启动 observer:{}", observer.toString()); } @Override public void onDirectoryCreate(File directory) { log.info("FileListener [新建]:path:{}", directory.getPath()); } @Override public void onDirectoryChange(File directory) { log.info("FileListener [修改]:path:{}", directory.getPath()); } @Override public void onDirectoryDelete(File directory) { log.info("FileListener [删除]:path:{}", directory.getPath()); } @Override public void onStop(FileAlterationObserver observer) { log.debug("FileListener 停止 observer:{}", observer.toString()); } @Override public void onFileCreate(File file) { log.info("FileListener [新建]:path:{}", file.getPath()); refreshProperties(); log.info("{}-文件新增,重新加载配置文件:{}", file.getName(), configDir); } @Override public void onFileChange(File file) { log.info("FileListener [修改]:path:{}", file.getPath()); if(file.getName().endsWith("properties")){ log.info("文件修改,重新加载配置文件:{}", file.getName()); refreshProperties(file.getPath()); } } @Override public void onFileDelete(File file) { log.info("FileListener [删除]:path:{}", file.getPath()); refreshProperties(); log.info("{}-文件删除,重新加载配置文件:{}", file.getName(), configDir); } private void refreshProperties(String... filePaths){ try { if(propertyConfig == null){ log.error("FileListener.refreshProperties propertyConfig 获取失败,无法刷新properties配置文件"); } else { if(filePaths.length > 0){ //修改文件 重新加载该文件 String filePath = filePaths[0]; int index = filePath.indexOf(configDir);//定位config目录的位置 String diyPath = filePath.substring(index); propertyConfig.loadPoint(diyPath); } else { //新增删除文件 控制overview.properties进行重新加载 propertyConfig.init(); } } } catch (BeansException e) { log.error("FileListener 刷新配置文件失败,path:{}", filePaths, e); } }}
3、编写配置类
import com.dingxianginc.channelaggregation.util.VariableConfig;import com.dingxianginc.channelaggregation.webconfig.properties.PropertyConfig;import lombok.extern.slf4j.Slf4j;import org.apache.commons.io.filefilter.FileFilterUtils;import org.apache.commons.io.monitor.FileAlterationMonitor;import org.apache.commons.io.monitor.FileAlterationObserver;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Configuration;import javax.annotation.PostConstruct;import java.io.File;import java.util.Objects;import java.util.concurrent.TimeUnit;/** * @author ysma 2019-07-30 * 监控 resouces目录下文件的变化 */@Slf4j@Configurationpublic class FileListenerConfig { // 轮询间隔 5 秒 private static final long INTERVAL = TimeUnit.SECONDS.toMillis(5); @Autowired private PropertyConfig propertyConfig; @Autowired private VariableConfig variableConfig; @PostConstruct public void init(){ //path尾缀 / String path = Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResource("")).getPath(); String configDir = variableConfig.getConfigDir(); String dir = configDir.startsWith("/")? path + configDir.substring(1) : path + configDir; monitor(dir); } /** * 目录和文件监控:遍历文件夹 递归监控 * @param dir 目录 */ public void monitor(String dir){ File resource = new File(dir); if (resource.isDirectory()){ File[] files = resource.listFiles(); if(files != null){ for(File file : files){ monitor(file.getPath()); } } log.info("监听文件目录:{}", dir); monitorFile(dir); } } /** * 监听文件变化 */ public void monitorFile(String rootDir){ try { //1.构造观察类主要提供要观察的文件或目录,当然还有详细信息的filter FileAlterationObserver observer = new FileAlterationObserver( rootDir, FileFilterUtils.or(FileFilterUtils.fileFileFilter(), FileFilterUtils.directoryFileFilter()) ); //2.配置监听 observer.addListener(new FileListener( propertyConfig, variableConfig.getConfigDir())); //3.配置Monitor,第一个参数单位是毫秒,是监听的间隔;第二个参数就是绑定我们之前的观察对象 FileAlterationMonitor monitor = new FileAlterationMonitor(INTERVAL, observer); //开始监控 monitor.start(); } catch (Exception e) { log.error("FileListenerConfig.wrapSimple 监听失败,rootDir:{}", rootDir, e); } }}
4、编写properties配置类
import com.dingxianginc.channelaggregation.util.VariableConfig;import lombok.extern.slf4j.Slf4j;import org.apache.commons.configuration.ConfigurationException;import org.apache.commons.configuration.PropertiesConfiguration;import org.apache.commons.lang3.StringUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cache.annotation.Cacheable;import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;import java.io.FileNotFoundException;import java.util.HashMap;import java.util.Iterator;import java.util.Map;/** * 配置文件组件 */@Slf4j@Componentpublic class PropertyConfig { private volatile MappropertyPathMap = new HashMap<>(); //扩展为caffeine缓存 避免hashMap产生运行时修改异常 private volatile Map propertyFieldMap = new HashMap<>(); @Autowired private VariableConfig variableConfig; @PostConstruct public void init(){ //根目录 loadPoint(variableConfig.getOverview()); } /** * 从path开始逐步加载所有 */ public PropertiesConfiguration loadPoint(String path){ try { //1.加载配置 PropertiesConfiguration config = PropertyUtil.loadProperty(path); if(config != null){ //2.遍历key集合 Iterator keys = config.getKeys(); while (keys.hasNext()){ String key = keys.next(); String[] fields = config.getStringArray(key);//默认按列表获取,默认英文逗号分隔, for(String field : fields){ if(StringUtils.isNotEmpty(field) && field.endsWith("properties")){ //4.递归实现 PropertiesConfiguration pointConfig = loadPoint(field); propertyFieldMap.put(field, pointConfig); } } } log.info("PropertyConfig.loadPoint path:{} 配置文件加载完毕", path); propertyPathMap.put(path, config); return config; } else { log.error("PropertyConfig.loadPoint path为空,请检查是否正确调用"); } } catch (ConfigurationException | FileNotFoundException e) { log.error("PropertyConfig.loadPoint 加载配置文件:{}失败", path, e); } return null; } /** * 设置caffeine缓存 * sync:设置如果缓存过期是不是只放一个请求去请求数据库,其他请求阻塞,默认是false * @return 优先返回缓存 * key的设置参见:https://blog.csdn.net/cr898839618/article/details/81109291 */ @Cacheable(value = "channel_agg" ,sync = true) public Map getPropertyPathMap(){ return propertyPathMap; } @Cacheable(value = "channel_agg", sync = true) public Map getPropertyFieldMap(){ return propertyFieldMap; }}
5、补充properties文件加载工具
import org.apache.commons.configuration.ConfigurationException;import org.apache.commons.configuration.PropertiesConfiguration;import org.apache.commons.lang3.StringUtils;import java.io.*;import java.net.URL;public class PropertyUtil { //加载文件的频率 单位:毫秒 private static final long RELOAD_PERIOD = 5000L; /** * getClassLoader().getResource方法就是在resources目录下查找 * 当传入值 path 以前缀/开头 则应使用class.getResource直接查询,否则使用class.getClassLoader().getResource进行查询 * @param path 文件路径 * @throws ConfigurationException Exception * @throws FileNotFoundException Exception */ public static PropertiesConfiguration loadProperty(String path) throws ConfigurationException, FileNotFoundException { //1.空判断 if(StringUtils.isEmpty(path)){ return null; } else { path = path.replaceAll("\\\\", "/");//以Linux路径为准 /** * 2.依据开头自主选择加载方法 * 第一:前面有 "/" 代表了工程的根目录,例如工程名叫做myproject,"/"代表了myproject * 第二:前面没有 "" 代表当前类的目录 */ URL url = path.startsWith("/") ? PropertyUtil.class.getResource(path) : PropertyUtil.class.getClassLoader().getResource(path); if(url == null){ throw new FileNotFoundException(path); } //3.加载配置文件 PropertiesConfiguration config = new PropertiesConfiguration(); //设置扫描文件的最小时间间隔 重新加载文件时会导致tomcat重启!!! /*FileChangedReloadingStrategy fileChangedReloadingStrategy = new FileChangedReloadingStrategy(); fileChangedReloadingStrategy.setRefreshDelay(RELOAD_PERIOD); config.setReloadingStrategy(fileChangedReloadingStrategy);*/ config.setAutoSave(true); //getResource和getResourceAsStream是有缓存的,这里重写文件流 config.load(new FileInputStream(url.getPath())); return config; } }}
6、properties文件配置类中引用了caffeine
依赖jar包:
org.springframework.boot spring-boot-starter-cache com.github.ben-manes.caffeine caffeine 2.7.0
7、简单配置:application.properties如下,Application启动类中启用缓存: @EnableCaching
spring.cache.type=caffeinespring.cache.cache-names=channel_aggspring.cache.caffeine.spec=initialCapacity=100,maximumSize=5000,expireAfterAccess=10s
8、caffeine配置参数详解
注解在Spring中的应用很广泛,几乎成为了其标志,这里说下使用注解来集成缓存。
caffeine cache方面的注解主要有以下5个
@Cacheable 触发缓存入口(这里一般放在创建和获取的方法上)
@CacheEvict 触发缓存的eviction(用于删除的方法上)
@CachePut 更新缓存且不影响方法执行(用于修改的方法上,该注解下的方法始终会被执行)
@Caching 将多个缓存组合在一个方法上(该注解可以允许一个方法同时设置多个注解)
@CacheConfig 在类级别设置一些缓存相关的共同配置(与其它缓存配合使用)
可参见:caffeine 通过spring注解方式引入
代码版简版如下:
import com.github.benmanes.caffeine.cache.CacheLoader;import com.github.benmanes.caffeine.cache.Caffeine;import org.springframework.cache.CacheManager;import org.springframework.cache.caffeine.CaffeineCacheManager;import java.util.Collections;import java.util.concurrent.TimeUnit;/*@Slf4j@Configuration@deprecated 使用配置方式 此方式留待扩展 配置方式测试见 @see ChannelAggregationApplicationTests.testCache*/@Deprecated@SuppressWarnings("all")public class CacheConfig { private static final String CACHE_NAME = "channel_agg"; //配置CacheManager /*@Bean(name = "caffeine")*/ public CacheManager cacheManagerWithCaffeine() { CaffeineCacheManager cacheManager = new CaffeineCacheManager(); /** * initialCapacity=[integer]: 初始的缓存空间大小 * maximumSize=[long]: 缓存的最大条数 * maximumWeight=[long]: 缓存的最大权重 * expireAfterAccess=[duration秒]: 最后一次写入或访问后经过固定时间过期 * expireAfterWrite=[duration秒]: 最后一次写入后经过固定时间过期 * refreshAfterWrite=[duration秒]: 创建缓存或者最近一次更新缓存后经过固定的时间间隔,刷新缓存 * weakKeys: 打开key的弱引用 * weakValues:打开value的弱引用 * softValues:打开value的软引用 * recordStats:开发统计功能 */ Caffeine caffeine = Caffeine.newBuilder() //cache的初始容量值 .initialCapacity(100) //maximumSize用来控制cache的最大缓存数量,maximumSize和maximumWeight(最大权重)不可以同时使用, .maximumSize(5000) //最后一次写入或者访问后过久过期 .expireAfterAccess(2, TimeUnit.HOURS) //创建或更新之后多久刷新,指定了refreshAfterWrite还需要设置cacheLoader .refreshAfterWrite(10, TimeUnit.SECONDS); cacheManager.setCaffeine(caffeine); cacheManager.setCacheLoader(cacheLoader()); cacheManager.setCacheNames(Collections.singletonList(CACHE_NAME));//根据名字可以创建多个cache,但是多个cache使用相同的策略 cacheManager.setAllowNullValues(false);//是否允许值为空 return cacheManager; } /** * 必须要指定这个Bean,refreshAfterWrite配置属性才生效 */ /*@Bean*/ public CacheLoader
上述就是小编为大家分享的springboot2 中怎么动态加载properties 文件了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注行业资讯频道。
文件
配置
缓存
方法
目录
注解
监听
监控
最大
多个
方式
时间
代表
参数
就是
更新
观察
动态
内容
单位
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
湖南服务器安全云空间
数据库 差运算
两台服务器自动备份切换
澳门导航软件开发
网络安全高级技术职称
计算机的网络安全实质
计算机网络技术过程控制
计算机网络技术专业学数学吗
株洲橙全网络技术有限公司
no数据库
五厘网络技术有限公司
腾讯云服务器被攻击
有关dhcp服务器的配置
进博会网络安全保障
乌鲁木齐代码审计网络安全培训
临沂天马网络技术有限公司怎么样
网络安全的风险来源
数据库url在哪复制
数据库有什么好用的软件
监控网络技术人员
济南应用软件开发一般要多少钱
选择数据库优缺点
网络技术教材2019变化
网络安全工具蜜罐
视频图像大数据库
国家网络安全活动ppt
linux服务器安全设置视频
网络安全教育时间
蚌埠市公安局网络安全保卫支队
计算机网络技术发展的特点