0Pricing
Java Academy · Lekcja

WatchService: monitorowanie zmian plików

Rejestruj WatchService, aby w czasie rzeczywistym wykrywać zdarzenia utworzenia, modyfikacji i usunięcia plików.

WatchService: monitorowanie zmian plików to bezpłatna lekcja Java Academy na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Java Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Java Academy zawiera 4 lekcji w sumie.

Czym jest WatchService?

WatchService monitoruje katalog pod kątem zdarzeń systemu plików: tworzenia, modyfikowania i usuwania plików. Korzysta z powiadomień na poziomie systemu operacyjnego (inotify w systemie Linux, FSEvents w systemie macOS) — jest to znacznie wydajniejsze niż odpytywanie.

import java.nio.file.*;

WatchService watcher = FileSystems.getDefault().newWatchService();
Path dir = Path.of("/home/user/watched");

dir.register(watcher,
    StandardWatchEventKinds.ENTRY_CREATE,
    StandardWatchEventKinds.ENTRY_MODIFY,
    StandardWatchEventKinds.ENTRY_DELETE
);
System.out.println("Watching: " + dir);

Odpytywanie zdarzeń

Wywołaj watcher.take(), aby zablokować wykonanie do momentu wystąpienia zdarzenia, a następnie przetwórz zdarzenia:

while (true) {
    WatchKey key;
    try {
        key = watcher.take(); // blocks until events arrive
    } catch (InterruptedException e) {
        break;
    }
    
    for (WatchEvent<?> event : key.pollEvents()) {
        WatchEvent.Kind<?> kind = event.kind();
        Path changed = ((WatchEvent<Path>) event).context();
        System.out.println(kind.name() + ": " + changed);
    }
    
    key.reset(); // must reset to receive further events
}

Nieblokujące odpytywanie

Użyj watcher.poll() do nieblokujących sprawdzeń albo poll(timeout, unit), aby zaczekać przez określony czas:

// Non-blocking — returns null if no events
WatchKey key = watcher.poll();
if (key != null) {
    // process events
    key.reset();
}

// Wait up to 5 seconds:
WatchKey key2 = watcher.poll(5, java.util.concurrent.TimeUnit.SECONDS);
if (key2 != null) {
    // process events
    key2.reset();
}

Zdarzenie OVERFLOW

Jeśli zdarzenia gromadzą się szybciej, niż aplikacja jest w stanie je przetwarzać, zdarzenie OVERFLOW sygnalizuje utratę części zdarzeń. Obsłuż je w kontrolowany sposób:

for (WatchEvent<?> event : key.pollEvents()) {
    if (event.kind() == StandardWatchEventKinds.OVERFLOW) {
        System.out.println("OVERFLOW: some events missed, rescanning directory");
        // rescan dir to find current state
        continue;
    }
    // process normal events
}

Pełny działający przykład

Kompletny obserwator plików działający w wątku w tle:

class FileWatcher implements Runnable {
    private final WatchService watcher;
    
    FileWatcher(Path dir) throws IOException {
        watcher = FileSystems.getDefault().newWatchService();
        dir.register(watcher,
            StandardWatchEventKinds.ENTRY_CREATE,
            StandardWatchEventKinds.ENTRY_MODIFY,
            StandardWatchEventKinds.ENTRY_DELETE);
    }
    
    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            try {
                WatchKey key = watcher.take();
                for (WatchEvent<?> e : key.pollEvents())
                    System.out.println(e.kind() + ": " + e.context());
                key.reset();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

Obserwowanie wielu katalogów

Zarejestruj wiele katalogów w ramach tego samego WatchService. Użyj zwróconego WatchKey, aby określić, z którego katalogu pochodzi zdarzenie:

Map<WatchKey, Path> keyMap = new HashMap<>();

for (Path dir : List.of(Path.of("/logs"), Path.of("/config"))) {
    WatchKey key = dir.register(watcher,
        StandardWatchEventKinds.ENTRY_MODIFY);
    keyMap.put(key, dir);
}

WatchKey key = watcher.take();
Path dir = keyMap.get(key);
System.out.println("Changed in: " + dir);

Uzyskiwanie pełnej ścieżki

Metoda context() zwraca tylko nazwę pliku, a nie pełną ścieżkę. Rozwiąż ją względem obserwowanego katalogu:

WatchEvent<Path> pathEvent = (WatchEvent<Path>) event;
Path filename = pathEvent.context();
Path fullPath = watchedDir.resolve(filename);
System.out.println("Full path: " + fullPath);

Rekurencyjne obserwowanie katalogów

WatchService obserwuje tylko zarejestrowany katalog, a nie jego podkatalogi. Aby obserwować katalogi rekurencyjnie, przejdź po drzewie i zarejestruj każdy podkatalog:

WatchService watcher = FileSystems.getDefault().newWatchService();
Path root = Path.of("/project");

try (var stream = Files.walk(root)) {
    stream.filter(Files::isDirectory)
          .forEach(dir -> {
              try { dir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE,
                  StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);
              } catch (IOException e) { throw new RuntimeException(e); }
          });
}

Zwalnianie zasobów

Po zakończeniu zawsze zamknij WatchService, aby zwolnić zasoby systemu operacyjnego:

try (WatchService watcher = FileSystems.getDefault().newWatchService()) {
    Path dir = Path.of("/tmp/watch");
    dir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE);
    
    WatchKey key = watcher.poll(10, java.util.concurrent.TimeUnit.SECONDS);
    if (key != null) {
        key.pollEvents().forEach(e -> System.out.println(e.context()));
        key.reset();
    }
} // watcher.close() called automatically

Praktyczny przypadek użycia: przeładowywanie konfiguracji

Użyj WatchService, aby przeładowywać konfigurację po zmianie pliku:

class ConfigWatcher {
    private volatile Map<String, String> config = new HashMap<>();
    
    void loadConfig(Path file) throws IOException {
        config = Files.readAllLines(file).stream()
            .filter(l -> l.contains("="))
            .collect(Collectors.toMap(
                l -> l.split("=")[0].trim(),
                l -> l.split("=")[1].trim(),
                (a,b) -> b
            ));
    }
    
    // Call loadConfig again on ENTRY_MODIFY event
}

Ograniczenia WatchService

Ważne ograniczenia:

  • Obserwuje tylko katalogi, a nie pojedyncze pliki
  • Domyślnie nie działa rekurencyjnie — każdy podkatalog trzeba zarejestrować ręcznie
  • W systemie macOS: wewnętrznie korzysta z odpytywania (jest mniej responsywny niż inotify w systemie Linux)
  • Może pomijać szybko następujące po sobie serie zmian (OVERFLOW)
  • WatchKey staje się nieprawidłowy po usunięciu obserwowanego katalogu

Szybkie sprawdzenie

Po przetworzeniu zdarzeń z WatchKey co należy wywołać, zanim WatchService będzie mógł dostarczyć kolejne zdarzenia dla tego klucza?

Podsumowanie: WatchService

Najważniejsze informacje:

  • Rejestruj katalogi dla zdarzeń ENTRY_CREATE/MODIFY/DELETE
  • take() blokuje wykonanie; poll() jest nieblokujące; poll(timeout, unit) służy do oczekiwania przez określony czas
  • Po przetworzeniu zdarzeń zawsze wywołuj key.reset()
  • context() zwraca tylko nazwę pliku — aby uzyskać pełną ścieżkę, rozwiąż ją względem obserwowanego katalogu
  • Zamykaj WatchService za pomocą try-with-resources
  • Obserwowanie rekurencyjne wymaga zarejestrowania każdego podkatalogu

Często zadawane pytania

Czy lekcja „WatchService: monitorowanie zmian plików” jest bezpłatna?

Tak — pełny tekst „WatchService: monitorowanie zmian plików” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Java Academy, przejdź na CoddyKit PRO. Kurs Java Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „WatchService: monitorowanie zmian plików”?

Rejestruj WatchService, aby w czasie rzeczywistym wykrywać zdarzenia utworzenia, modyfikacji i usunięcia plików. Ćwiczysz Java Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Java Academy?

Nie wymagamy żadnego doświadczenia. Java Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.

Ile czasu zajmuje lekcja „WatchService: monitorowanie zmian plików”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Java Academy?

Tak. Każda lekcja Java Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Path: reprezentowanie lokalizacji plików
  2. Narzędzie Files: odczyt, zapis, kopiowanie i przenoszenie
  3. Przechodzenie po drzewach katalogów za pomocą Files.walk
  4. WatchService: monitorowanie zmian plików
← Powrót do Java Academy