WatchService: مراقبة تغييرات الملفات
سجّل WatchService لاكتشاف أحداث إنشاء الملفات وتعديلها وحذفها في الوقت الفعلي
WatchService: مراقبة تغييرات الملفات درس مجاني في Java Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Java Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Java Academy 4 دروس في المجموع.
ما هي WatchService؟
تراقب WatchService دليلًا بحثًا عن أحداث نظام الملفات: الإنشاء والتعديل والحذف. وتستخدم إشعارات على مستوى نظام التشغيل (inotify على Linux وFSEvents على macOS)، وهي أكثر كفاءة بكثير من الاستطلاع.
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);الاستطلاع بحثًا عن الأحداث
استدعِ watcher.take() للانتظار حتى وقوع حدث، ثم عالج الأحداث:
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
}الاستطلاع غير الحاجب
استخدم watcher.poll() لإجراء فحوصات غير حاجبة، أو poll(timeout, unit) للانتظار لمدة محددة:
// 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();
}حدث OVERFLOW
إذا تراكمت الأحداث بمعدل أسرع من قدرة التطبيق على معالجتها، يشير حدث OVERFLOW إلى فقدان بعض الأحداث. عالج ذلك بصورة سليمة:
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
}مثال عملي كامل
مراقب ملفات متكامل يعمل في خيط تنفيذ في الخلفية:
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();
}
}
}
}مراقبة أدلة متعددة
سجّل أدلة متعددة باستخدام WatchService نفسه. استخدم WatchKey المُعاد لتحديد الدليل الذي صدر منه الحدث:
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);حل المسار الكامل
يعيد context() الخاص بالحدث اسم الملف فقط، وليس المسار الكامل. حلّه بالنسبة إلى الدليل المراقَب:
WatchEvent<Path> pathEvent = (WatchEvent<Path>) event;
Path filename = pathEvent.context();
Path fullPath = watchedDir.resolve(filename);
System.out.println("Full path: " + fullPath);المراقبة التكرارية للأدلة
يراقب WatchService الدليل المسجَّل فقط، وليس أدلته الفرعية. وللمراقبة التكرارية، تجوّل في الشجرة وسجّل كل دليل فرعي:
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); }
});
}تنظيف الموارد
أغلق WatchService دائمًا عند الانتهاء لتحرير موارد نظام التشغيل:
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حالة استخدام عملية: إعادة تحميل الإعدادات تلقائيًا
استخدم WatchService لإعادة تحميل الإعدادات عند تغيّر الملف:
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
}قيود WatchService
القيود المهمة:
- يراقب الأدلة فقط، وليس الملفات الفردية
- لا يعمل بصورة تكرارية تلقائيًا — يجب تسجيل كل دليل فرعي يدويًا
- على macOS: يستخدم الاستطلاع داخليًا، ولذلك تكون استجابته أبطأ من inotify على Linux
- قد يفوّت دفعات التغييرات السريعة (OVERFLOW)
- يصبح WatchKey غير صالح إذا حُذف الدليل المراقَب
اختبار سريع
بعد معالجة الأحداث من WatchKey، ما الذي يجب استدعاؤه قبل أن يعيد WatchService إرسال أحداث إضافية لذلك المفتاح؟
مراجعة: WatchService
أهم النقاط:
- سجّل الأدلة مع أحداث ENTRY_CREATE/MODIFY/DELETE
- take() حاجب؛ وpoll() غير حاجب؛ أما poll(timeout, unit) فللانتظار لمدة محددة
- استدعِ key.reset() دائمًا بعد معالجة الأحداث
- يعيد context() اسم الملف فقط — حلّه بالنسبة إلى الدليل المراقَب للحصول على المسار الكامل
- أغلق WatchService باستخدام try-with-resources
- تتطلب المراقبة التكرارية تسجيل كل دليل فرعي
الأسئلة الشائعة
هل درس «WatchService: مراقبة تغييرات الملفات» مجاني؟
نعم — نص درس «WatchService: مراقبة تغييرات الملفات» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Java Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Java Academy 4 دروس في المجموع.
ماذا ستتعلم في «WatchService: مراقبة تغييرات الملفات»؟
سجّل WatchService لاكتشاف أحداث إنشاء الملفات وتعديلها وحذفها في الوقت الفعلي تتمرن على Java Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Java Academy؟
لا تُشترط خبرة سابقة. Java Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.
كم من الوقت يستغرق درس «WatchService: مراقبة تغييرات الملفات»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Java Academy هذا؟
نعم. كل درس في Java Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- Path: تمثيل مواقع الملفات
- أداة Files: القراءة والكتابة والنسخ والنقل
- استعراض أشجار المجلدات باستخدام Files.walk
- WatchService: مراقبة تغييرات الملفات