You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 13:25:19