package com.haogrgr.test.util; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Properties; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import javax.annotation.PreDestroy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationEvent; import org.springframework.context.event.EventListener; import org.springframework.core.io.Resource; /** * 应用配置属性类, 配置刷新时会向Spring发送AppConfigReloadedEvent事件. * * @author desheng.tu * @since 2015年8月27日 下午3:37:48 * */ public class AppConfig implements ApplicationContextAware, InitializingBean { private static Logger logger = LoggerFactory.getLogger(AppConfig.class); private volatile Map<String, String> config; private Resource configLocation; //配置文件 private Integer refreshDelaySeconds = 180; //默认180秒刷新 private final Lock lock = new ReentrantLock(); private ApplicationContext context; private ScheduledExecutorService scheduler; private boolean setScheduler = false; //外部的scheduler由外部关闭 private ScheduledFuture<?> watcher; public String getStr(String key) { String val = getValue(key); return val; } public String getStr(String key, String defaultValue) { String val = getValue(key); return val != null ? val : defaultValue; } public Integer getInt(String key) { String val = getValue(key); return val == null ? null : Integer.valueOf(val); } public Integer getInt(String key, Integer defaultValue) { String val = getValue(key); return val == null ? defaultValue : Integer.valueOf(val); } //key不为null判断 private String getValue(String key) { Objects.requireNonNull(key, "key不能为null"); return config.get(key); } //set, afterPropertiesSet public void setConfigLocation(Resource configLocation) { this.configLocation = configLocation; } public void setRefreshDelaySeconds(Integer refreshDelaySeconds) { this.refreshDelaySeconds = refreshDelaySeconds; } public void setScheduler(ScheduledExecutorService scheduler) { this.scheduler = scheduler; this.setScheduler = true;//谁创建, 谁关闭 } //spring @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = applicationContext; } @Override public void afterPropertiesSet() throws Exception { Objects.requireNonNull(configLocation, "配置文件地址不能为空"); //初始化properties loadConfig(); //自动刷新检查定时任务 if (refreshDelaySeconds > 0) { Objects.requireNonNull(configLocation.getFile(), "配置文件不能为空");//自动刷新时, 配置文件不能在jar中 if (scheduler == null) { scheduler = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory()); } watcher = scheduler.scheduleAtFixedRate(new ConfigMonitorTask(this, configLocation.getFile()), refreshDelaySeconds, refreshDelaySeconds, TimeUnit.SECONDS); } printDebugLog(); } //从properties配置文件中加载配置 private void loadConfig() throws IOException { Properties props = new Properties(); InputStream input = configLocation.getInputStream(); try { props.load(input); } finally { input.close(); } HashMap<String, String> map = new HashMap<>(props.size()); for (Map.Entry<Object, Object> entry : props.entrySet()) { Objects.requireNonNull(entry.getValue()); map.put((String) entry.getKey(), (String) entry.getValue()); } this.config = map; } /** * 输出日志输出 */ @EventListener public void onConfigReloaded(AppConfigReloadedEvent event) { printDebugLog(); } /** * debug输出配置 */ private void printDebugLog() { try { logger.info("加载配置文件 : " + configLocation.getURL()); if (logger.isDebugEnabled()) { StringBuilder sb = new StringBuilder(); for (Entry<String, String> entry : config.entrySet()) { sb.append('\n').append(entry.getKey()).append('=').append(entry.getValue()); } logger.debug(sb.toString()); } } catch (IOException e) { logger.error("重新加载配置文件 : 获取配置文件路径错误"); } } @PreDestroy public void cancelWatcher() { if (watcher != null) { watcher.cancel(true); watcher = null; } if (scheduler != null && !setScheduler) { scheduler.shutdownNow(); } } //自定义线程工厂 private static class NamedThreadFactory implements ThreadFactory { private AtomicInteger inc = new AtomicInteger(0); @Override public Thread newThread(Runnable r) { return new Thread(r, "appconfig-" + inc.getAndIncrement()); } } //定时任务, 根据文件修改时间, 刷新配置 private static class ConfigMonitorTask implements Runnable { private final AppConfig config; private final File file; private volatile long lastModified; public ConfigMonitorTask(AppConfig config, File file) { this.config = config; this.file = file; this.lastModified = file.lastModified(); } @Override public void run() { config.lock.lock(); try { final long currentLastModified = file.lastModified(); if (currentLastModified <= lastModified) { return; } lastModified = currentLastModified; config.loadConfig(); //发布配置重新加载 config.context.publishEvent(new AppConfigReloadedEvent(config)); } catch (IOException e) { logger.error("重新加载配置文件异常", e); } finally { config.lock.unlock(); } } } /** * 配置重新加载完成事件 */ public static class AppConfigReloadedEvent extends ApplicationEvent { private static final long serialVersionUID = 1L; public AppConfigReloadedEvent(AppConfig config) { super(config); } public AppConfig getAppConfig() { return (AppConfig) this.getSource(); } } }