您好,登錄后才能下訂單哦!
這期內(nèi)容當(dāng)中小編將會給大家?guī)碛嘘P(guān)springboot2 中怎么動態(tài)加載properties 文件,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
1、比較好的方案,采用文件監(jiān)控 依賴 commons-io2
<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.5</version> </dependency>
2、編寫監(jiān)聽器
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; /** * 自定義文件監(jiān)聽器 * @author ysma */ @Slf4j @Component public 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進(jìn)行重新加載 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 * 監(jiān)控 resouces目錄下文件的變化 */ @Slf4j @Configuration public 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); } /** * 目錄和文件監(jiān)控:遍歷文件夾 遞歸監(jiān)控 * @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("監(jiān)聽文件目錄:{}", dir); monitorFile(dir); } } /** * 監(jiān)聽文件變化 */ public void monitorFile(String rootDir){ try { //1.構(gòu)造觀察類主要提供要觀察的文件或目錄,當(dāng)然還有詳細(xì)信息的filter FileAlterationObserver observer = new FileAlterationObserver( rootDir, FileFilterUtils.or(FileFilterUtils.fileFileFilter(), FileFilterUtils.directoryFileFilter()) ); //2.配置監(jiān)聽 observer.addListener(new FileListener( propertyConfig, variableConfig.getConfigDir())); //3.配置Monitor,第一個參數(shù)單位是毫秒,是監(jiān)聽的間隔;第二個參數(shù)就是綁定我們之前的觀察對象 FileAlterationMonitor monitor = new FileAlterationMonitor(INTERVAL, observer); //開始監(jiān)控 monitor.start(); } catch (Exception e) { log.error("FileListenerConfig.wrapSimple 監(jiān)聽失敗,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 @Component public class PropertyConfig { private volatile Map<String, PropertiesConfiguration> propertyPathMap = new HashMap<>(); //擴(kuò)展為caffeine緩存 避免hashMap產(chǎn)生運行時修改異常 private volatile Map<String, PropertiesConfiguration> 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<String> keys = config.getKeys(); while (keys.hasNext()){ String key = keys.next(); String[] fields = config.getStringArray(key);//默認(rèn)按列表獲取,默認(rèn)英文逗號分隔, for(String field : fields){ if(StringUtils.isNotEmpty(field) && field.endsWith("properties")){ //4.遞歸實現(xiàn) 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為空,請檢查是否正確調(diào)用"); } } catch (ConfigurationException | FileNotFoundException e) { log.error("PropertyConfig.loadPoint 加載配置文件:{}失敗", path, e); } return null; } /** * 設(shè)置caffeine緩存 * sync:設(shè)置如果緩存過期是不是只放一個請求去請求數(shù)據(jù)庫,其他請求阻塞,默認(rèn)是false * @return 優(yōu)先返回緩存 * key的設(shè)置參見:https://blog.csdn.net/cr898839618/article/details/81109291 */ @Cacheable(value = "channel_agg" ,sync = true) public Map<String, PropertiesConfiguration> getPropertyPathMap(){ return propertyPathMap; } @Cacheable(value = "channel_agg", sync = true) public Map<String, PropertiesConfiguration> getPropertyFieldMap(){ return propertyFieldMap; } }
5、補(bǔ)充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目錄下查找 * 當(dāng)傳入值 path 以前綴/開頭 則應(yīng)使用class.getResource直接查詢,否則使用class.getClassLoader().getResource進(jìn)行查詢 * @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路徑為準(zhǔn) /** * 2.依據(jù)開頭自主選擇加載方法 * 第一:前面有 "/" 代表了工程的根目錄,例如工程名叫做myproject,"/"代表了myproject * 第二:前面沒有 "" 代表當(dāng)前類的目錄 */ 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(); //設(shè)置掃描文件的最小時間間隔 重新加載文件時會導(dǎo)致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包:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <!-- caffeine --> <dependency> <groupId>com.github.ben-manes.caffeine</groupId> <artifactId>caffeine</artifactId> <version>2.7.0</version> </dependency>
7、簡單配置:application.properties如下,Application啟動類中啟用緩存: @EnableCaching
spring.cache.type=caffeine spring.cache.cache-names=channel_agg spring.cache.caffeine.spec=initialCapacity=100,maximumSize=5000,expireAfterAccess=10s
8、caffeine配置參數(shù)詳解
注解在Spring中的應(yīng)用很廣泛,幾乎成為了其標(biāo)志,這里說下使用注解來集成緩存。
caffeine cache方面的注解主要有以下5個
@Cacheable 觸發(fā)緩存入口(這里一般放在創(chuàng)建和獲取的方法上)
@CacheEvict 觸發(fā)緩存的eviction(用于刪除的方法上)
@CachePut 更新緩存且不影響方法執(zhí)行(用于修改的方法上,該注解下的方法始終會被執(zhí)行)
@Caching 將多個緩存組合在一個方法上(該注解可以允許一個方法同時設(shè)置多個注解)
@CacheConfig 在類級別設(shè)置一些緩存相關(guān)的共同配置(與其它緩存配合使用)
可參見: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 使用配置方式 此方式留待擴(kuò)展 配置方式測試見 @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]: 緩存的最大條數(shù) * maximumWeight=[long]: 緩存的最大權(quán)重 * expireAfterAccess=[duration秒]: 最后一次寫入或訪問后經(jīng)過固定時間過期 * expireAfterWrite=[duration秒]: 最后一次寫入后經(jīng)過固定時間過期 * refreshAfterWrite=[duration秒]: 創(chuàng)建緩存或者最近一次更新緩存后經(jīng)過固定的時間間隔,刷新緩存 * weakKeys: 打開key的弱引用 * weakValues:打開value的弱引用 * softValues:打開value的軟引用 * recordStats:開發(fā)統(tǒng)計功能 */ Caffeine caffeine = Caffeine.newBuilder() //cache的初始容量值 .initialCapacity(100) //maximumSize用來控制cache的最大緩存數(shù)量,maximumSize和maximumWeight(最大權(quán)重)不可以同時使用, .maximumSize(5000) //最后一次寫入或者訪問后過久過期 .expireAfterAccess(2, TimeUnit.HOURS) //創(chuàng)建或更新之后多久刷新,指定了refreshAfterWrite還需要設(shè)置cacheLoader .refreshAfterWrite(10, TimeUnit.SECONDS); cacheManager.setCaffeine(caffeine); cacheManager.setCacheLoader(cacheLoader()); cacheManager.setCacheNames(Collections.singletonList(CACHE_NAME));//根據(jù)名字可以創(chuàng)建多個cache,但是多個cache使用相同的策略 cacheManager.setAllowNullValues(false);//是否允許值為空 return cacheManager; } /** * 必須要指定這個Bean,refreshAfterWrite配置屬性才生效 */ /*@Bean*/ public CacheLoader<Object, Object> cacheLoader() { return new CacheLoader<Object, Object>() { @Override public Object load(Object key) throws Exception { return null;} // 重寫這個方法將oldValue值返回回去,進(jìn)而刷新緩存 @Override public Object reload(Object key, Object oldValue) throws Exception { return oldValue; } }; } }
上述就是小編為大家分享的springboot2 中怎么動態(tài)加載properties 文件了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。