0Pricing
Java Academy · درس

الحقن عبر المُنشئ والتبعيات الدائرية

فضّل الحقن عبر المُنشئ لتحسين قابلية الاختبار، واكتشف التبعيات الدائرية، وعالجها باستخدام @Lazy

الحقن عبر المُنشئ والتبعيات الدائرية درس مجاني في Java Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Java Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Java Academy 4 دروس في المجموع.

أنواع الحقن الثلاثة

يدعم Spring حقن المُنشئ (وهو الموصى به)، وحقن الحقل (وضع @Autowired على حقل)، وحقن setter. ويُفضَّل حقن المُنشئ لقابلية الاختبار وعدم قابلية التغيير.

حقن المُنشئ

أعلن عن التبعيات باعتبارها معاملات للمُنشئ. يحقنها Spring تلقائيًا. وإذا كان هناك مُنشئ واحد فقط، تكون @Autowired اختيارية (في Spring Boot 2.1 والإصدارات الأحدث).

@Service
public class OrderService {
    private final OrderRepository repo;
    private final EmailService email;
    // @Autowired is optional when there is only one constructor
    public OrderService(OrderRepository repo, EmailService email) {
        this.repo  = repo;
        this.email = email;
    }
}

لماذا يُفضَّل حقن المُنشئ

يوفّر حقن المُنشئ: (1) تبعيات نهائية — أي غير قابلة للتغيير، (2) فئة مهيّأة بالكامل دائمًا، (3) عدم وجود تبعية لـSpring في اختبارات الوحدة — فما عليك سوى استدعاء new OrderService(mockRepo, mockEmail).

// Unit test — no Spring context needed:
OrderRepository mockRepo  = mock(OrderRepository.class);
EmailService    mockEmail = mock(EmailService.class);
OrderService    service   = new OrderService(mockRepo, mockEmail);
// Test service logic directly

حقن الحقل (تجنّبه)

يتميز حقن الحقل باستخدام @Autowired بالاختصار، لكنه يسبب مشكلات: فلا يمكن أن تكون الحقول final، ولا يكون الـBean مهيّأً بالكامل بعد إنشائه، كما يتطلب اختبار الوحدة استخدام Spring أو أدوات المساعدة الانعكاسية في Mockito.

// Avoid in production code:
@Service
public class UserService {
    @Autowired  // hidden dependency
    private UserRepository repo; // cannot be final
}

حقن setter للتبعيات الاختيارية

استخدم حقن setter للتبعيات الاختيارية التي لها قيمة افتراضية مناسبة — وضع @Autowired(required = false) على setter.

@Service
public class NotificationService {
    private SmsSender smsSender; // optional
    @Autowired(required = false)
    public void setSmsSender(SmsSender s) { this.smsSender = s; }
}

ما التبعية الدائرية؟

تحدث التبعية الدائرية عندما تتطلب A‏ B وتتطلب B‏ A (بشكل مباشر أو عبر سلسلة من التبعيات). يكتشف Spring ذلك عند بدء التشغيل ويطرح BeanCurrentlyInCreationException.

// CIRCULAR:
@Service class A { A(B b) {} }
@Service class B { B(A a) {} }
// Spring throws: BeanCurrentlyInCreationException: Is there an unresolvable circular reference?

كسر الدائرية: إعادة الهيكلة إلى خدمة ثالثة

الحل الأنظف هو استخراج المنطق المشترك إلى خدمة ثالثة لا تعتمد عليها A أو B بطريقة دورية.

// Before: A <-> B
// After: A -> C, B -> C (no cycle)
@Service class SharedLogic { ... }
@Service class A { A(SharedLogic s) {} }
@Service class B { B(SharedLogic s) {} }

كسر الدائرية: @Lazy

أضف التعليق التوضيحي @Lazy إلى إحدى نقاط الحقن. ينشئ Spring وكيلًا لـB فورًا، ولا يُنشئ B الحقيقي إلا عندما تستدعي A طريقة فيه لأول مرة.

@Service
public class A {
    private final B b;
    public A(@Lazy B b) { this.b = b; } // breaks the cycle
}

كسر الدائرية: البحث عبر ApplicationContext

احقن ApplicationContext في أحد الـBeans، وابحث عن الـBean الآخر عند الحاجة باستخدام context.getBean() — لكن ذلك يقلل من قابلية الاختبار والوضوح.

@Service
public class A implements ApplicationContextAware {
    private ApplicationContext ctx;
    public void setApplicationContext(ApplicationContext c) { this.ctx = c; }
    public void doWork() { ctx.getBean(B.class).help(); }
}

حقن setter لكسر الدائرية

تُنشأ Beans التي تستخدم حقن setter أولًا (فيكتمل الإنشاء)، ثم تُعيَّن التبعيات. ويسمح ذلك بالتبعيات الدائرية على حساب قابلية التغيير.

@Service
public class A {
    private B b;
    @Autowired public void setB(B b) { this.b = b; } // Spring sets after construction
}

spring.main.allow-circular-references (Spring Boot 2.6+)

لا يسمح Spring Boot 2.6 والإصدارات الأحدث بالتبعيات الدائرية افتراضيًا. فعّلها صراحةً فقط كتدبير مؤقت أثناء إعادة الهيكلة: spring.main.allow-circular-references=true.

# application.properties (temporary workaround only):
spring.main.allow-circular-references=true

فحص سريع

لماذا يُفضَّل حقن المُنشئ على حقن الحقل؟

مراجعة

فضّل حقن المُنشئ: فهو غير قابل للتغيير، وقابل للاختبار، وصريح. تجنّب حقن الحقل. استخدم @Lazy أو استخرج خدمة مشتركة لكسر التبعيات الدائرية. يبلّغ Spring Boot 2.6 والإصدارات الأحدث عن المراجع الدائرية عند بدء التشغيل — فاعتبرها تحذيرات تصميم.

الأسئلة الشائعة

هل درس «الحقن عبر المُنشئ والتبعيات الدائرية» مجاني؟

نعم — نص درس «الحقن عبر المُنشئ والتبعيات الدائرية» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Java Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Java Academy 4 دروس في المجموع.

ماذا ستتعلم في «الحقن عبر المُنشئ والتبعيات الدائرية»؟

فضّل الحقن عبر المُنشئ لتحسين قابلية الاختبار، واكتشف التبعيات الدائرية، وعالجها باستخدام @Lazy تتمرن على Java Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Java Academy؟

لا تُشترط خبرة سابقة. Java Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.

كم من الوقت يستغرق درس «الحقن عبر المُنشئ والتبعيات الدائرية»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Java Academy هذا؟

نعم. كل درس في Java Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. التهيئة التلقائية وSpring Boot Starters
  2. خصائص التطبيق وملفات التعريف
  3. توصيل الـ beans: ‏@Component و@Service و@Repository
  4. الحقن عبر المُنشئ والتبعيات الدائرية
← العودة إلى Java Academy