أساسيات Spring Boot

أدوات المطوّر وإعادة التحميل الفوري

18 دقيقة الدرس 9 من 13

أدوات المطوّر وإعادة التحميل الفوري

كل ثانية تقضيها في انتظار إعادة تشغيل JVM الكاملة هي ثانية مسروقة من التطوير الفعلي. تهاجم وحدة spring-boot-devtools في Spring Boot هذه المشكلة من عدة زوايا: إعادة تحميل الفئات تلقائيًا، وتحديث الإعدادات فوريًا، وإبقاء جلسة HTTP نشطة بين عمليات إعادة التشغيل، ومجموعة من الإعدادات الافتراضية الملائمة لبيئة التطوير — كل ذلك دون أي تأثير على بنية الإنتاج.

إضافة DevTools إلى مشروعك

تأتي DevTools كـ starter قياسي. أضفها إلى pom.xml:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency>

أو في Gradle (build.gradle):

developmentOnly 'org.springframework.boot:spring-boot-devtools'
لماذا optional أو developmentOnly؟ تستبعد مهمة إعادة تغليف Spring Boot تلقائيًا جميع التبعيات الاختيارية وdevelopmentOnly من ملف fat-JAR القابل للتشغيل. لن تصل DevTools أبدًا إلى مسار الفئات في الإنتاج — لا خطر مطلقًا من تفعيل سلوك إعادة التشغيل في بيئة حية.

إعادة التشغيل التلقائية — كيف تعمل

تراقب DevTools الملفات الموجودة على مسار الفئات. فور اكتشاف تغيير — ملف .class معاد تجميعه، أو ملف .properties أو قالب معدَّل — تُطلق عملية إعادة تشغيل التطبيق. الفكرة الجوهرية هي استخدام محمِّلَي فئات (ClassLoaders):

  • محمِّل الفئات الأساسي (Base ClassLoader) — يحمّل مكتبات الطرف الثالث (Spring وHibernate والمكتبات الأخرى). هذه نادرًا ما تتغير، لذا لا يُتجاهل محمِّل الفئات الأساسي بين عمليات إعادة التشغيل. يُنشأ مرة واحدة ويبقى نشطًا.
  • محمِّل فئات إعادة التشغيل (Restart ClassLoader) — يحمّل كود تطبيقك الخاص (الفئات في target/classes). هذا يُتجاهل ويُعاد إنشاؤه عند كل إعادة تشغيل.

لأن ثلثَي تكلفة بدء JVM (تحميل وتهيئة بايتكود الطرف الثالث) تُحذف، يكون وقت إعادة التشغيل الفعلي 1–3 ثوانٍ عادةً بدلًا من 10–20 ثانية لتشغيل JVM نظيف.

شغِّل إعادة التشغيل من IDE وليس من نظام الملفات. لا تُجري IntelliJ IDEA تجميعًا عند الحفظ افتراضيًا. فعِّل Build > Build Project Automatically ثم نشِّط Advanced Settings > Allow auto-make (IDEA 2021+). VS Code مع إضافة Java يُجري تجميعًا تدريجيًا ويُطلق إعادة تشغيل DevTools تلقائيًا.

ضبط مسارات المراقبة

تراقب DevTools بشكل افتراضي كل شيء على مسار الفئات. يمكنك ضبط ذلك في application.properties:

# المجلدات المستبعدة من مشغّل إعادة التشغيل (الأصول الثابتة والقوالب) spring.devtools.restart.exclude=static/**,public/**,templates/** # مراقبة مسار إضافي ليس على مسار الفئات spring.devtools.restart.additional-paths=src/main/resources/extra # تعطيل إعادة التشغيل كليًّا (إذا أردت LiveReload فقط) spring.devtools.restart.enabled=false

استبعاد static/** وtemplates/** تحسينٌ شائع: تغيير قالب Thymeleaf أو ملف CSS لا يستلزم إعادة تشغيل على مستوى JVM — يكفي تحديث بسيط في المتصفح. إبقاء هذه المسارات خارج مشغّل إعادة التشغيل يجعل الدورة أسرع.

تكامل LiveReload

تُضمِّن DevTools خادم LiveReload على المنفذ 35729. ثبِّت إضافة المتصفح المجانية LiveReload (Chrome/Firefox/Safari) وسيُحدِّث متصفحك الصفحة الحالية تلقائيًا بعد كل إعادة تشغيل. حتى لتغييرات القوالب والأصول الثابتة التي لم تُشغِّل إعادة تشغيل، ترسل DevTools إشارة إعادة التحميل.

# تعطيل خادم LiveReload المُضمَّن spring.devtools.livereload.enabled=false
تعارض في المنفذ؟ إذا كان شيء آخر يستمع بالفعل على المنفذ 35729، اضبط spring.devtools.livereload.port=<منفذ-آخر> وحدِّث إعدادات إضافة LiveReload لتتطابق.

DevTools عن بُعد

يمكن لـ DevTools أيضًا الاتصال بتطبيق بعيد — خادم تجريبي يشغِّل JAR مع devtools — ودفع إشارات إعادة التشغيل عبر HTTP. هذا مفيد في سير العمل المبني على Docker حيث تحرِّر محليًا لكن التطبيق يعمل في حاوية.

فعِّل الدعم البعيد في التطبيق الذي تنشره (غير موصى به على الإنترنت العام، فقط للشبكات الداخلية أو المحمية بـ VPN):

# application.properties على الخادم البعيد spring.devtools.remote.secret=my-secret-token

ثم شغِّل مُطلِق RemoteSpringApplication محليًا موجِّهًا إياه إلى عنوان URL البعيد. في كل مرة تُعيد فيها التجميع محليًا تُرفع الفئات المتغيرة ويُعيد الخادم البعيد التشغيل خلال ثوانٍ.

لا تعرِّض نقطة نهاية DevTools البعيدة على الإنترنت العام أبدًا. يُرسَل السر البعيد عبر HTTP (أو HTTPS إذا هيّأته). يستطيع مهاجم يعرف السر تشغيل رفع فئات عشوائية. أبقِ DevTools البعيد حصريًا داخل الشبكات الخاصة أو شبكات Docker.

تجاوزات الخصائص في وقت التطوير

تُطبِّق DevTools تلقائيًا مجموعة من الإعدادات الافتراضية المعقولة عند اكتشاف بيئة تطوير. من أبرز التجاوزات:

  • تعطيل تخزين Thymeleaf مؤقتًا (spring.thymeleaf.cache=false) — تغييرات القوالب مرئية فورًا.
  • تعطيل التخزين المؤقت لـ FreeMarker وGroovy وMustache وweb MVC أيضًا.
  • ضبط مستوى التسجيل لمجموعة الويب على DEBUG لعرض تعيينات الطلبات وتنفيذ المعالج وتحليل العرض في الوحدة الطرفية.
  • تفعيل H2 console إذا كان H2 موجودًا على مسار الفئات.

يمكنك الاطلاع على القائمة الكاملة في DevToolsPropertyDefaultsPostProcessor في شيفرة المصدر لـ Spring Boot. تُطبَّق هذه التجاوزات عبر PropertySource ذات الأولوية الأدنى، لذا تتقدم دائمًا أي قيمة تضبطها صراحةً في application.properties.

إبقاء الجلسات نشطة عبر عمليات إعادة التشغيل

تُسلسِل DevTools بشكل افتراضي جلسة HTTP على القرص قبل إعادة التشغيل وتُزيل تسلسلها بعده. هذا يعني بقاءك مسجَّل الدخول عبر كل دورة إعادة تشغيل — لا داعي لإعادة المصادقة بعد كل حفظ. يمكنك إلغاء الاشتراك:

spring.devtools.restart.persist-sessions=false

إعدادات DevTools العالمية

إذا كنت تعمل على مشاريع متعددة وتريد إعداد DevTools مشتركًا واحدًا، أنشئ ملفًا في ~/.config/spring-boot/spring-boot-devtools.properties (Spring Boot 2.3+) أو المسار القديم ~/.spring-boot-devtools.properties. تسري الإعدادات هنا على جميع المشاريع الممكَّنة بـ DevTools على الجهاز:

# ~/.config/spring-boot/spring-boot-devtools.properties spring.devtools.restart.trigger-file=.trigger

نمط ملف المشغّل (trigger file) مفيد بشكل خاص في IDEs التي تُجري تجميعًا مستمرًا: بدلًا من إعادة التشغيل عند كل تغيير في ملف فئة فردي، تُعيد DevTools التشغيل فقط عند لمس ملف محدد (مثل .trigger). هذا يمنع عمليات إعادة التشغيل الجزئية أثناء التجميعات متعددة الملفات.

DevTools مقابل JRebel مقابل Spring Loaded

DevTools مجانية وبلا تهيئة مسبقة وكافية لغالبية المشاريع. JRebel منتج تجاري يستبدل الفئات بدون إعادة تشغيل سياق Spring — مفيد للتطبيقات الكبيرة جدًا حيث حتى إعادة التشغيل بثانيتين تُعطِّل سير العمل. Spring Loaded هو سلف مفتوح المصدر صار مستبدَلًا إلى حد بعيد بـ DevTools. لمعظم الفرق، تُحقِّق DevTools التوازن المثالي.

الخلاصة

تُقلِّص spring-boot-devtools حلقة التطوير الداخلية من إعادة تشغيل كاملة تستغرق 15 ثانية إلى إعادة تحميل سياق في 1–3 ثوانٍ باستخدام استراتيجية محمِّلَي الفئات. اقرنها بالتجميع التلقائي في IDE وإضافة LiveReload للمتصفح وستحصل على دورة تغذية راجعة شبه فورية. ضع التبعية كـ optional (Maven) أو developmentOnly (Gradle) ولن تصل أبدًا إلى fat-JAR الإنتاجي. في الدرس التالي والأخير ستجمع كل شيء معًا ببناء أول تطبيق Spring Boot كامل من الصفر.