一下代码整合springboot properties 属性实现热加载功能,在不重启项目的情况下修改application.properties 里边的属性达到修改后的属性整合进spring propertie enviroment 的效果
@Configuration
@ConditionalOnProperty(matchIfMissing = true,//Default if no , pushing startup
prefix = "heat.exacutio.master",
name = "deploy",
havingValue = "true")
public class AutoNotifyHeatReloadWatcherConfiguration {
@Autowired
Environment environment;
private static final Logger LOGGER = LoggerFactory.getLogger(AutoNotifyHeatReloadWatcherConfiguration.class);
@Bean(initMethod = "init")
@ConditionalOnMissingBean
public RecompileNotifyWatcher Watcher() {
if (!System.getProperty(OS_NAME).equalsIgnoreCase(LINUX)){return null;}
LOGGER.info("Init RecompileNotifyWatcher of event notify based on unix poll ");
return new RecompileNotifyWatcher(environment);
}
}
/**
* Example to watch a directory (or tree) for changes to files.
*
* @date 19-7-15
* @auther jack
* @description TODO
*/
@Slf4j
public final class RecompileNotifyWatcher {
private WatchService watcher;
private Map keys;
private boolean recursive;
private boolean trace;
private Environment environment;
private List pPatternContainer = Arrays.asList(".properties");
public RecompileNotifyWatcher(Environment environment) {
try {
this.watcher = FileSystems.getDefault().newWatchService();
this.keys = new HashMap<>(8);
this.recursive = true;
this.environment = environment;
} catch (Exception e) {
log.error("Watcher initialized error !", e);
System.exit(-1);
}
}
private RecompileNotifyWatcher (){
}
/**
* Creates a WatchService and registers the given directory
*/
public RecompileNotifyWatcher(Path dir, boolean recursive, Environment environment) {
this(environment);
this.initRegisterAboutRecusive(dir, recursive);
this.processEvents();
}
/**
* Register the given directory with the WatchService
*/
private void register(Path dir) throws IOException {
WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
if (trace) {
Path prev = keys.get(key);
if (prev == null) {
log.debug("Path of directory register is {}\n", dir);
} else {
if (!dir.equals(prev)) {
log.debug("update directory register: %s -> {}\n", prev, dir);
}
}
}
keys.put(key, dir);
}
/**
* Register the given directory, and all its sub-directories, with the
* WatchService.
*/
private void registerAll(final Path start) throws IOException {
// register directory and sub-directories
Files.walkFileTree(start, new SimpleFileVisitor() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException {
register(dir);
return FileVisitResult.CONTINUE;
}
});
}
private void initRegisterAboutRecusive(Path dir, boolean recursive) {
try {
if (recursive) {
log.info("Scanning register directory for {} ...\n", dir);
this.registerAll(dir);
log.info("Register Done.");
} else {
register(dir);
}
} catch (IOException e) {
log.error("Running construct method matches error~");
System.exit(-1);
}
/* enable trace after initial registration */
this.trace = true;
}
WatchEvent cast(WatchEvent> event) {
return (WatchEvent) event;
}
/**
* Process all events for keys queued to the watcher
*/
public void processEvents() {
for ( ; ; ) {
/* wait for key to be signalled */
WatchKey key;
try {
key = watcher.take();
log.debug("Hold on that for starting signal key {}", key.toString());
} catch (InterruptedException x) {
return;
}
//key.
Path dir = keys.get(key);
if (dir == null) {
log.info("WatchKey haven't been recognized!!");
continue;
}
/* StartUp to heat deploy logically ~ */
try {
/* Delete key event to be handled */
//if (!this.catchDeleteEvent(key)) {
for (WatchEvent> event : key.pollEvents()) {
WatchEvent.Kind kind = event.kind();
log.debug("Current event be catched ,which's {} type", kind.type());
/* TBD - provide example of how OVERFLOW event is handled */
if (kind == OVERFLOW) {
continue;
}
if (kind == ENTRY_DELETE){
log.info("Delete entry signal was be catched for {}", ENTRY_DELETE.name());
this.doCompile();
break ;
}
//TODO Need to refine a major event in many incidents
Iterator iterator = pPatternContainer.iterator();
boolean flag = false;
/* Context for directory entry event is the file name of entry */
Path child = this.handleInvocableEventPath(dir, event);
log.info("The real path of watchable is {},this path is Path before analysis",child.toFile().getAbsolutePath());
//filter [^*\\.property | ^*\\.class]
while (iterator.hasNext()){
if (child.toString().endsWith(iterator.next())){
flag = true;
break;
}
}
if (!flag){
continue ;
}
/* Path to the event about handling for validation */
if (this.formalPathValidate(child)) {
/* property hot reload */
this.PropertyReloadThroughPath(child);
/* if directory is created, and watching recursively, then register it and its sub-directories */
if (recursive && (kind == ENTRY_CREATE)) {
try {
if (Files.isDirectory(child, NOFOLLOW_LINKS)) {
registerAll(child);
}
} catch (IOException x) {
/* ignore to keep sample readable */
}
}
}
}
} catch (Exception e) {
log.error("Internal errors occur", e);
e.fillInStackTrace();
continue;
/* Ignore error , but only to print error stack message */
}
/* reset key and remove from set if directory no longer accessible */
boolean valid = key.reset();
if (!valid) {
keys.remove(key);
/* all directories are inaccessible */
if (keys.isEmpty()) {
break;
}
}
}
}
private boolean formalPathValidate(Path dir) {
return dir.toString().endsWith(SUBFFIX_PROPERTIES) ||
dir.toString().endsWith(SUBFFIX_JAVA);
}
/* Path analysis */
private Path handleInvocableEventPath(Path dir, WatchEvent> event) {
WatchEvent ev = this.cast(event);
Path name = ev.context();
return dir.resolve(name);
}
/**
* Load the spring cloud config configuration and integrate the properties of the hot deployment
*
* @param path
* @throws Exception
*/
protected void PropertyReloadThroughPath(Path path) throws Exception {
this.doCompile();
assert environment instanceof StandardServletEnvironment;
StandardServletEnvironment standardServletEnvironment = (StandardServletEnvironment) environment;
synchronized (standardServletEnvironment) {
MutablePropertySources mutablePropertySources = standardServletEnvironment.getPropertySources();
PropertySource propertySource = mutablePropertySources.get("applicationConfig: [classpath:/application");
Map maps = ((OriginTrackedMapPropertySource) propertySource).getSource();
if (path == null ||
path.toFile().length() == 0 ||
!path.toString().endsWith(SUBFFIX_PROPERTIES)) {
return;
}
try (InputStream uls = path.
toUri().
toURL().
openStream()) {
Properties p = new Properties();
p.load(uls);
log.info("Extra value map has been added into environment,newly loaded value is {}", p.toString());
assert p instanceof Map;
maps.putAll((Map) p);
} catch (Exception e) {
/* If the path is just moving, it is possible to detect an abnormal flow. just ignore*/
return;
}
}
}
/**
* pre-compile-handle
*
* @throws IOException
* @throws InterruptedException
*/
private void doCompile() throws Exception {
//SystemUtils.IS_OS_LINUX
if (!this.isLinux()){return;}
Process process = Runtime.getRuntime().exec(DEPLOY_STATIC_ACTION);
int status = process.waitFor();
if (status != 0) {
log.error("Failed to call shell's command");
}
try (BufferedReader var = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
while (var.readLine() != null) {
log.info(var.readLine());
}
}
}
private boolean isLinux() {
return System.getProperty(OS_NAME).equalsIgnoreCase(LINUX);
}
private void init() {
Path dir = Paths.get(System.getProperty(USER_DIR) + File.separator + "src");
Thread deason = new Thread(()-> {
try {
new RecompileNotifyWatcher(dir, true,environment);
} catch (Exception e) {
//ignore
log.warn("Initialized of heat-reload exocutia has been occurs error , please attend to check");
}
});
deason.setDaemon(true);
deason.setName("exacutio");
}
}