如何在Spring Boot项目启动成功后启动目录文件监听代码
在Spring Boot启动后运行目录监听代码的方案
你可以通过Spring Boot提供的ApplicationRunner或CommandLineRunner接口,在应用启动完成后自动执行目录监听逻辑,核心是要把监听代码放到独立线程中,避免阻塞主线程。
原监听代码:
private static String folderPath = "D:\\Import"; public static void main(final String[] args) throws IOException, InterruptedException { System.out.println("Running file verifier"); System.out.println("monitoring folder " + folderPath); EntityImportRequestsJob sql = new EntityImportRequestsJob(); WatchService watchService = FileSystems.getDefault().newWatchService(); Path path = Paths.get(folderPath); path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY); WatchKey key; while ((key = watchService.take()) != null) { for (WatchEvent<?> event : key.pollEvents()) { System.out.println("Event kind:" + event.kind() + ". File affected: " + event.context() + "."); if (event.kind().equals(StandardWatchEventKinds.ENTRY_DELETE)) { Instant start = Instant.now(); boolean flag = true; while(flag) { while ((key = watchService.take()) != null) { HashMap<String, List> map = sql.checkFileImport(); List values = map.get(event.context()); // get values by file name if(values.contains("Completed")){ // exit the monitoring while loop flag = false; } } Thread.sleep(1000); } Instant end = Instant.now(); System.out.println(Duration.between(start,end)); long seconds = TimeUnit.MILLISECONDS.toSeconds(Duration.between(start,end).getSeconds()); long minutes = TimeUnit.MILLISECONDS.toMinutes(Duration.between(start,end).getSeconds()); System.out.format("Execution time %d minutes %d seconds", minutes, seconds); } } key.reset(); } watchService.close(); }
改造后的Spring Boot实现:
创建一个实现ApplicationRunner的组件,将监听逻辑封装到独立线程中:
import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; import java.io.IOException; import java.nio.file.*; import java.time.Duration; import java.time.Instant; import java.util.HashMap; import java.util.List; import java.util.concurrent.TimeUnit; @Component public class DirectoryWatcher implements ApplicationRunner { private final String folderPath = "D:\\Import"; private final EntityImportRequestsJob entityImportRequestsJob; // 依赖注入EntityImportRequestsJob,替换原代码的new操作 public DirectoryWatcher(EntityImportRequestsJob entityImportRequestsJob) { this.entityImportRequestsJob = entityImportRequestsJob; } @Override public void run(org.springframework.boot.ApplicationArguments args) { // 启动独立线程执行监听,避免阻塞Spring Boot主线程 Thread watcherThread = new Thread(this::startDirectoryWatch); watcherThread.setDaemon(true); // 设置为守护线程,应用关闭时自动终止 watcherThread.start(); } private void startDirectoryWatch() { try { System.out.println("Running file verifier"); System.out.println("monitoring folder " + folderPath); WatchService watchService = FileSystems.getDefault().newWatchService(); Path path = Paths.get(folderPath); path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY); WatchKey key; while ((key = watchService.take()) != null) { for (WatchEvent<?> event : key.pollEvents()) { System.out.println("Event kind:" + event.kind() + ". File affected: " + event.context() + "."); if (event.kind().equals(StandardWatchEventKinds.ENTRY_DELETE)) { Instant start = Instant.now(); boolean flag = true; while (flag) { // 调整原逻辑,避免内层循环覆盖外层key导致的异常 HashMap<String, List> map = entityImportRequestsJob.checkFileImport(); List values = map.get(event.context()); if (values != null && values.contains("Completed")) { flag = false; } if (flag) { Thread.sleep(1000); } } Instant end = Instant.now(); Duration duration = Duration.between(start, end); System.out.println(duration); long totalSeconds = duration.getSeconds(); long minutes = TimeUnit.SECONDS.toMinutes(totalSeconds); long remainingSeconds = totalSeconds % 60; System.out.format("Execution time %d minutes %d seconds", minutes, remainingSeconds); } } key.reset(); } watchService.close(); } catch (IOException | InterruptedException e) { Thread.currentThread().interrupt(); System.err.println("Directory watcher stopped with error: " + e.getMessage()); } } }
关键注意事项:
@Component注解会让Spring自动扫描并注册这个Bean,应用启动时会触发run方法。- 必须将监听逻辑放到独立线程中,否则会阻塞Spring Boot的启动流程,导致应用无法正常初始化。
- 把原代码中
new EntityImportRequestsJob()改为构造函数注入,符合Spring的依赖管理规范,便于后续扩展和测试。 - 调整了原代码中嵌套的
watchService.take()循环,避免覆盖外层的WatchKey导致逻辑异常,改为每隔1秒查询一次状态。
内容的提问来源于stack exchange,提问作者Peter Penzov
相关产品推荐
相关产品推荐

