🔥 FREE PRO OFFER OnlyLink.click Pro Version is 100% Free of Cost till 31 December, 2026! Claim Free Pro

حقن التبعية في Quarkus باستخدام CDI: ArC والنطاقات والمؤهلات وحقن وقت البناء

حقن التبعية في Quarkus باستخدام CDI: ArC والنطاقات والمؤهلات وحقن وقت البناء

يعد حقن التبعية (DI) حجر الأساس لتطوير تطبيقات Java للمؤسسات الحديثة. لسنوات عديدة، اعتمد مطورو Java على Spring IoC (انعكاس التحكم) أو حاويات Jakarta EE CDI القياسية (السياقات وحقن التبعية) مثل Hibernate Weld أو OpenWebBeans لفصل دورات حياة المكونات، وإدارة تبعيات الخدمة، وحقن خصائص التكوين.

ومع ذلك، مع تحول تطبيقات المؤسسات نحو الخدمات الصغيرة السحابية الأصلية، واستخدام الحاويات مع Docker وKubernetes، ونماذج التنفيذ بدون خادم (AWS Lambda، Knative)، كشفت حاويات حقن التبعية التقليدية عن خلل فادح: الانعكاس الشديد في وقت التشغيل وعبء كبير للذاكرة.

أدخل Quarkus ومحرك CDI خفيف الوزن، ArC. تم تصميم Quarkus من الألف إلى الياء للتجميع الأصلي لـ Kubernetes وGraalVM، وهو يعيد تعريف حقن التبعية عن طريق تحويل الانعكاس ومسح مسار الفصل وأسلاك الرسم البياني للفاصوليا من وقت تشغيل التطبيق إلى وقت البناء.

في هذا الدليل التفصيلي، سوف نستكشف حقن التبعية في Quarkus باستخدام CDI، الذي يغطي الآليات الداخلية لـحاوية ArC، ونطاقات CDI، والمؤهلات المخصصة، والمنتجين، والمعترضين، والمراقبين المستندين إلى الأحداث، وعمليات البحث الآلية، وأفضل الممارسات لكتابة تطبيقات Java السحابية الأصلية عالية الأداء.


1. فهم ArC: محرك Quarkus Build-Time CDI

تعمل حاويات Jakarta EE CDI القياسية بالكامل عند بدء تشغيل التطبيق. عند تشغيل تطبيق قديم، تقوم حاوية CDI بمسح كل JAR في مسار الفصل، وتوزع بيانات التعريف، وتفحص التعليقات التوضيحية باستخدام Java Reflection (java.lang.reflect)، وتنشئ وكلاء ديناميكيين في الذاكرة، وتنشئ الرسم البياني للتبعية. يستهلك هذا الانعكاس الديناميكي قدرًا كبيرًا من ذاكرة الوصول العشوائي (Metaspace) ويزيد من وقت التشغيل البارد.

يحل Quarkus عنق الزجاجة هذا باستخدام ArC (تطبيق CDI في وقت البناء استنادًا إلى مواصفات Jakarta CDI 4.0).

وقت التشغيل التقليدي CDI مع الانعكاس مقابل Quarkus ArC Build-Time Wiring

كيف يختلف ARC عن CDI القياسي:

  1. تعزيز وقت البناء: أثناء mvn package أو ./gradlew build، يقوم ARC بمسح جميع وحدات التطبيق، والتحقق من صحة نقاط الحقن، وإنشاء الرسم البياني للتبعية مسبقًا.
  2. ** إنشاء أكواد بايت قبل الوقت (AOT) **: يقوم ArC بإنشاء كود Java بايت مباشر لإنشاء مثيل، ووكلاء الفول، والاعتراضات مقدمًا، مما يمنع الانعكاس في وقت التشغيل.
  3. ** تقليم الفول غير المستخدم (إزالة الكود الميت) **: يتم التعرف على أي حبة CDI لم يتم حقنها أو الرجوع إليها ديناميكيًا أثناء التجميع ويتم تجريدها تلقائيًا من الملف الثنائي النهائي، مما يقلل من مساحة الذاكرة.
  4. GraalVM Native Image Optimization: نظرًا لأنه يتم حل أسلاك الفول في وقت الإنشاء، يتم تجميع تطبيقات Quarkus بسلاسة إلى ملفات تنفيذية أصلية بدون تلميحات تكوين انعكاس.

2. نطاقات CDI Bean وإدارة دورة الحياة

في CDI، يحدد النطاق دورة حياة مثيل الفول وإمكانية رؤيته. يدعم Quarkus ArC نطاقات Jakarta CDI القياسية مع تحسينات وقت البناء.

نطاقات Quarkus CDI Bean ومخطط دورات الحياة

نطاقات CDI الأساسية في Quarkus:

شرح النطاق دورة الحياة والسلوك إنشاء الوكيل أفضل حالة استخدام
@ApplicationScoped مثيل واحد لكل تطبيق. التهيئة البطيئة عند الاستدعاء الأول. الدولة مشتركة عبر جميع المواضيع. تم إنشاء وكيل العميل الخدمات والمستودعات والتكوينات عديمة الحالة.
@RequestScoped تم ربط المثيل بطلب HTTP واحد أو استدعاء حدث واحد. دمرت عند اكتمال الطلب. تم إنشاء وكيل العميل جلسات المستخدم، طلب بيانات السياق، الرموز الأمنية.
@معال النطاق الافتراضي إذا لم يتم تحديد أي شيء. ترتبط المثيلة مباشرة بعمر حبة الحقن. لا يوجد وكيل (مثيل مباشر) فئات المرافق المساعدة، أصحاب الدولة سريعة الزوال.
@سينجلتون مثيل واحد لكل تطبيق. تم مثيله بفارغ الصبر أو بتكاسل. لا يوجد غلاف وكيل العميل. لا يوجد وكيل (مثيل مباشر) مفردات عالية الأداء حيث يجب تجنب الحمل الوكيل للعميل.
@غير قابل للإزالة تعليق توضيحي للعلامة يوجه ArC بعدم إزالة الحبة أثناء تحسين وقت البناء. يعتمد على النطاق أهداف البحث الديناميكي أو الفاصوليا المرجعية للانعكاس.

مثال على الكود: الإعلان عن الفاصوليا ذات النطاق

package com.ghaznix.services;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.context.RequestScoped;
import java.util.UUID;

@ApplicationScoped
public class OrderProcessingService {
    
    public String processOrder(String orderId) {
        return "Order " + orderId + " processed successfully.";
    }
}

@RequestScoped
public class RequestContextHolder {
    private final String requestId = UUID.randomUUID().toString();

    public String getRequestId() {
        return requestId;
    }
}

3. أنماط حقن التبعية في Quarkus

يدعم Quarkus أنماط حقن متعددة باستخدام التعليق التوضيحي القياسي jakarta.inject.Inject.

أ. حقن المُنشئ (أفضل الممارسات الموصى بها)

يضمن حقن المُنشئ عدم قابلية التغيير (حقول final)، ويبسط اختبار الوحدة دون الحاجة إلى حاويات CDI، ويجعل التبعيات واضحة.

package com.ghaznix.resources;

import com.ghaznix.services.OrderProcessingService;
import jakarta.inject.Inject;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;

@Path("/orders")
public class OrderResource {

    private final OrderProcessingService orderService;

    // In Quarkus, if a class has a single constructor, @Inject is OPTIONAL!
    @Inject
    public OrderResource(OrderProcessingService orderService) {
        this.orderService = orderService;
    }

    @POST
    @Path("/{id}")
    public String checkout(@PathParam("id") String id) {
        return orderService.processOrder(id);
    }
}

نصيحة احترافية: في Quarkus ArC، إذا كانت الحبة تحتوي على منشئ واحد فقط، فيمكنك حذف التعليق التوضيحي @Inject تمامًا. يتعرف عليه ArC تلقائيًا باعتباره مُنشئ الحقن.

ب. الحقن الميداني

يعد الحقن الميداني موجزًا ​​ولكنه يجعل الحقول قابلة للتغيير ويعقد اختبار الوحدة المستقلة.

@ApplicationScoped
public class NotificationService {

    @Inject
    EmailClient emailClient; // Field injection
}

4. المؤهلات وتوضيح عمليات التنفيذ المتعددة

عندما تقوم عدة حبوب بتنفيذ نفس الواجهة، فإن CDI يلقي استثناء تبعية غامضًا في وقت الإنشاء. يستخدم Quarkus المؤهلات لحل عمليات تنفيذ الحبة المستهدفة بأمان.

تعريف المؤهلات المخصصة

package com.ghaznix.payment;

import jakarta.inject.Qualifier;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface PaymentType {
    Mode value();

    enum Mode {
        CREDIT_CARD,
        PAYPAL,
        CRYPTO
    }
}

تنفيذ الفاصوليا المؤهلة

public interface PaymentProcessor {
    void process(double amount);
}

@ApplicationScoped
@PaymentType(PaymentType.Mode.CREDIT_CARD)
public class CreditCardProcessor implements PaymentProcessor {
    @Override
    public void process(double amount) {
        System.out.println("Processing Credit Card payment: $" + amount);
    }
}

@ApplicationScoped
@PaymentType(PaymentType.Mode.PAYPAL)
public class PayPalProcessor implements PaymentProcessor {
    @Override
    public void process(double amount) {
        System.out.println("Processing PayPal payment: $" + amount);
    }
}

حقن الفاصوليا المؤهلة

@ApplicationScoped
public class CheckoutService {

    private final PaymentProcessor creditCardProcessor;
    private final PaymentProcessor payPalProcessor;

    @Inject
    public CheckoutService(
            @PaymentType(PaymentType.Mode.CREDIT_CARD) PaymentProcessor creditCardProcessor,
            @PaymentType(PaymentType.Mode.PAYPAL) PaymentProcessor payPalProcessor) {
        this.creditCardProcessor = creditCardProcessor;
        this.payPalProcessor = payPalProcessor;
    }
}

5. المنتجون والمتخلصون: حقن كائنات الطرف الثالث

تحتاج أحيانًا إلى إدخال فئات من مكتبات الجهات الخارجية (على سبيل المثال، عملاء AWS SDK، واتصالات Redis، ووحدات التكوين المخصصة) التي لم يتم إضافة تعليقات توضيحية إليها بـ @ApplicationScoped. يتعامل Quarkus مع هذا باستخدام طرق الإنتاج والمتخلصات.

package com.ghaznix.config;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Disposes;
import jakarta.enterprise.inject.Produces;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

@ApplicationScoped
public class DatabaseConnectionProducer {

    @Produces
    @ApplicationScoped
    public Connection createDatabaseConnection() throws SQLException {
        return DriverManager.getConnection("jdbc:postgresql://localhost:5432/mydb", "user", "pass");
    }

    // Disposer method called automatically when bean is destroyed
    public void closeConnection(@Disposes Connection connection) throws SQLException {
        if (connection != null && !connection.isClosed()) {
            connection.close();
        }
    }
}

6. اعتراضات CDI: البرمجة الموجهة نحو الجوانب (AOP)

تسمح أدوات الاعتراض بربط الاهتمامات الشاملة (التسجيل، ومسارات التدقيق، والمقاييس، وتحديد المعدل) بأساليب الفول دون تلويث منطق الأعمال.

الخطوة 1: تعريف ربط المعترض

package com.ghaznix.interceptors;

import jakarta.interceptor.InterceptorBinding;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@InterceptorBinding
@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface LogExecutionTime {
}

الخطوة الثانية: تنفيذ منطق الاعتراض

package com.ghaznix.interceptors;

import jakarta.annotation.Priority;
import jakarta.interceptor.AroundInvoke;
import jakarta.interceptor.Interceptor;
import jakarta.interceptor.InvocationContext;

@LogExecutionTime
@Interceptor
@Priority(Interceptor.Priority.APPLICATION)
public class LogExecutionTimeInterceptor {

    @AroundInvoke
    public Object logInvocation(InvocationContext context) throws Exception {
        long startTime = System.currentTimeMillis();
        try {
            return context.proceed();
        } finally {
            long duration = System.currentTimeMillis() - startTime;
            System.out.printf("[AUDIT] Method %s executed in %d ms%n",
                    context.getMethod().getName(), duration);
        }
    }
}

الخطوة 3: قم بتعليق الفاصوليا المستهدفة

@ApplicationScoped
public class AnalyticsService {

    @LogExecutionTime
    public void computeAggregates() {
        // Business computation
    }
}

نظرًا لأن Quarkus ينشئ وكلاء اعتراضيين ثابتين في وقت الإنشاء، فإن حمل استدعاء الطريقة يكون صفرًا تقريبًا مقارنة بوكلاء وقت التشغيل الديناميكي CGLIB في Spring Boot.


7. بنية تعتمد على الأحداث مع أحداث CDI

يوفر CDI نشر الأحداث المتزامنة وغير المتزامنة للاتصالات المنفصلة أثناء العملية.

نشر الأحداث ومراقبتها

// 1. Define Event Payload
public record OrderCreatedEvent(String orderId, double totalAmount) {}

// 2. Publish Event
@ApplicationScoped
public class OrderService {

    @Inject
    Event<OrderCreatedEvent> orderEventPublisher;

    public void createOrder(String orderId, double amount) {
        // Create order logic...
        orderEventPublisher.fire(new OrderCreatedEvent(orderId, amount));
    }
}

// 3. Observe Event Synchronously or Asynchronously
@ApplicationScoped
public class InventoryObserver {

    public void onOrderCreated(@Observes OrderCreatedEvent event) {
        System.out.println("Updating inventory for order: " + event.orderId());
    }

    public void onOrderCreatedAsync(@ObservesAsync OrderCreatedEvent event) {
        System.out.println("Async audit log for order: " + event.orderId());
    }
}

أحداث دورة حياة التطبيق

يكشف Quarkus عن أحداث بدء التشغيل وإيقاف التشغيل لتنفيذ تعليمات برمجية للتهيئة عند تشغيل التطبيق أو إنهائه:

@ApplicationScoped
public class ApplicationLifecycleBean {

    void onStart(@Observes StartupEvent ev) {
        System.out.println("Quarkus Application starting up...");
    }

    void onStop(@Observes ShutdownEvent ev) {
        System.out.println("Quarkus Application shutting down...");
    }
}

8. البحث البرمجي عن الفول باستخدام Instance<T> وArc.container()

عندما يجب اختيار التطبيقات المستهدفة ديناميكيًا في وقت التشغيل (أو داخل مثيلات قديمة غير CDI)، استخدم البحث الثابت Instance<T> أو Quarkus Arc.container().

@ApplicationScoped
public class DynamicPluginRegistry {

    @Inject
    Instance<PaymentProcessor> processors;

    public void executeAll(double amount) {
        for (PaymentProcessor processor : processors) {
            processor.process(amount);
        }
    }
}

9. المقارنة المعمارية: Quarkus ArC vs Spring IoC vs Weld

ميزة كواركوس آر سي حاوية الربيع IoC لحام جاكرتا القياسي
** حل التبعية ** وقت البناء (AOT) انعكاس وقت التشغيل انعكاس وقت التشغيل
** مساحة ذاكرة الوصول العشوائي الزائدة ** منخفض للغاية (حوالي 0 ميجابايت للبيانات التعريفية) عالية (ذاكرة التخزين المؤقت الديناميكية لفئة الوكيل) معتدلة إلى عالية
وقت البدء ميلي ثانية (<0.05 ثانية) ثانية (2.5 ثانية - 8 ثانية) ثانية (1.5 ثانية - 4 ثانية)
** دعم GraalVM الأصلي ** أصلي خارج الصندوق يتطلب تلميحات الربيع الأصلية التكوين المعقد
** تقليم الفاصوليا غير المستخدمة ** التقليم التلقائي في وقت البناء أبقى في Classpath أبقى في Classpath
** حقن منشئ واحد ** ضمني (لا حاجة إلى تعليق توضيحي) المدعومة يتطلب @Inject

10. وحدة اختبار CDI Beans باستخدام @QuarkusTest و@InjectMock

يجعل Quarkus اختبار حبوب CDI أمرًا سلسًا باستخدام JUnit 5 وRestAssured. استخدم @InjectMock لاستبدال وحدات التبعية بنماذج Mockito تلقائيًا أثناء الاختبار.

package com.ghaznix;

import com.ghaznix.services.OrderProcessingService;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

@QuarkusTest
public class OrderServiceTest {

    @Inject
    OrderService orderService;

    @InjectMock
    OrderProcessingService orderProcessingService;

    @Test
    public void testOrderCheckout() {
        Mockito.when(orderProcessingService.processOrder("ORD-101"))
               .thenReturn("Mocked Success");

        String result = orderService.checkout("ORD-101");
        Assertions.assertEquals("Mocked Success", result);
    }
}

11. الاستنتاج

يمثل حقن التبعية في Quarkus باستخدام CDI (ArC) تحولًا هائلاً في تصميم Java للمؤسسات. من خلال نقل مسح البيانات الوصفية للفاصوليا، وتوليد الوكيل، ودقة الرسم البياني للحقن إلى وقت التجميع، يوفر Quarkus سرعات بدء تشغيل تفوق سرعة الصوت، وآثار ذاكرة دون ذرية، و**دعمًا ثنائيًا أصليًا لـ GraalVM **.

سواء كنت تقوم بالترحيل من Spring Boot أو إنشاء خدمات صغيرة سحابية أصلية على Kubernetes، فإن إتقان Quarkus CDI يزودك بالأداء وإنتاجية المطورين المطلوبة لبنية البرامج الحديثة.

Ghaznix Ecosystem Products

Empower Your Digital Presence & Workflows

Explore top-tier tools built by Ghaznix to streamline your links, surveys, and brand growth.