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

Iniezione di dipendenze in Quarkus con CDI: ArC, ambiti, qualificatori e Build-Time Injection

Iniezione di dipendenze in Quarkus con CDI: ArC, ambiti, qualificatori e Build-Time Injection

Il Dependency Injection (DI) è il fondamento del moderno sviluppo di applicazioni Java aziendali. Per anni, gli sviluppatori Java si sono affidati a Spring IoC (Inversion of Control) o ai contenitori standard Jakarta EE CDI (Contexts and Dependency Injection) come Hibernate Weld o OpenWebBeans per disaccoppiare i cicli di vita dei componenti, gestire le dipendenze dei servizi e inserire proprietà di configurazione.

Tuttavia, con la transizione delle applicazioni aziendali verso microservizi nativi del cloud, containerizzazione con Docker e Kubernetes e modelli di esecuzione serverless (AWS Lambda, Knative), i contenitori tradizionali di inserimento delle dipendenze hanno messo in luce un difetto critico: pesante riflessione sul runtime e sovraccarico di memoria elevato.

Inserisci Quarkus e il suo leggero motore CDI, ArC. Costruito da zero per la compilazione nativa di Kubernetes e GraalVM, Quarkus ridefinisce l’inserimento delle dipendenze spostando la riflessione, la scansione dei percorsi di classe e il cablaggio del grafico dei bean dal runtime dell’applicazione al momento della creazione.

In questa guida approfondita, esploreremo l’Iniezione delle dipendenze in Quarkus con CDI, coprendo i meccanismi interni del contenitore ArC, gli ambiti CDI, i qualificatori personalizzati, i produttori, gli intercettori, gli osservatori guidati dagli eventi, le ricerche programmatiche e le best practice per scrivere applicazioni Java native del cloud ad alte prestazioni.


1. Comprendere ArC: motore CDI Quarkus Build-Time

I contenitori CDI Jakarta EE standard funzionano interamente all’avvio dell’applicazione. Quando si avvia un’applicazione legacy, il contenitore CDI esegue la scansione di ogni JAR nel classpath, analizza i metadati, controlla le annotazioni utilizzando Java Reflection (java.lang.reflect), genera proxy dinamici in memoria e crea il grafico delle dipendenze. Questa riflessione dinamica consuma una quantità significativa di RAM (Metaspace) e aumenta il tempo di avvio con avvio a freddo.

Quarkus risolve questo collo di bottiglia con ArC (un’implementazione CDI in fase di compilazione basata sulle specifiche Jakarta CDI 4.0).

CDI runtime tradizionale con cablaggio Reflection vs Quarkus ArC Build-Time

Differenze tra ArC e CDI standard:

  1. Aumento in fase di compilazione: durante mvn package o ./gradlew build, ArC esegue la scansione di tutti i bean dell’applicazione, convalida i punti di iniezione e precostruisce il grafico delle dipendenze.
  2. Generazione di bytecode Ahead-Of-Time (AOT): ArC genera bytecode Java diretto per l’istanziazione, proxy bean e interceptor in anticipo, eliminando la riflessione in fase di runtime.
  3. Purning dei bean inutilizzati (eliminazione del codice morto): qualsiasi bean CDI che non viene mai iniettato o referenziato dinamicamente viene identificato durante la compilazione e automaticamente rimosso dal binario finale, riducendo l’ingombro della memoria.
  4. Ottimizzazione dell’immagine nativa di GraalVM: poiché il cablaggio del bean viene risolto in fase di creazione, le applicazioni Quarkus si compilano perfettamente in eseguibili nativi con suggerimenti di configurazione a riflessione zero.

2. Ambiti del bean CDI e gestione del ciclo di vita

In CDI, uno Scope determina il ciclo di vita e la visibilità di un’istanza di bean. Quarkus ArC supporta gli ambiti CDI Jakarta standard con ottimizzazioni in fase di creazione.

Diagramma degli ambiti e dei cicli di vita dei bean CDI Quarkus

Ambiti CDI primari in Quarkus:

Annotazione dell’ambito Ciclo di vita e comportamento Creazione delega Miglior caso d’uso
“@ApplicationScoped”. Singola istanza per applicazione. Inizializzazione pigra alla prima invocazione. Stato condiviso tra tutti i thread. Proxy cliente generato Servizi stateless, repository, configurazioni.
“@RequestScoped”. Istanza associata a una singola richiesta HTTP o invocazione di un evento. Distrutto al completamento della richiesta. Proxy cliente generato Sessioni utente, richiesta di dati contestuali, token di sicurezza.
“@Dipendente”. Ambito predefinito se non specificato. Istanza legata direttamente alla durata del bean di iniezione. Nessun proxy (istanza diretta) Classi di utilità helper, detentori di stati effimeri.
“@Singleton”. Singola istanza per applicazione. Istanziato con impazienza o pigramente. Nessun wrapper proxy client. Nessun proxy (istanza diretta) Singleton ad alte prestazioni in cui è necessario evitare il sovraccarico del proxy client.
“@Inamovibile”. Annotazione marcatore che indica ad ArC di non rimuovere il bean durante l’ottimizzazione in fase di compilazione. Dipende dall’ambito Destinazioni di ricerca dinamica o bean con riferimenti alla riflessione.

Esempio di codice: dichiarazione di bean con ambito

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. Modelli di inserimento delle dipendenze in Quarkus

Quarkus supporta più stili di iniezione utilizzando l’annotazione standard jakarta.inject.Inject.

A. Iniezione del costruttore (migliore pratica consigliata)

L’inserimento del costruttore garantisce l’immutabilità (campi final), semplifica i test unitari senza richiedere contenitori CDI e rende esplicite le dipendenze.

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);
    }
}

Suggerimento da professionista: In Quarkus ArC, se un bean ha un solo un costruttore, puoi omettere completamente l’annotazione @Inject. ArC lo riconosce automaticamente come costruttore di iniezione.

B. Iniezione sul campo

L’iniezione sul campo è concisa ma rende i campi modificabili e complica i test unitari autonomi.

@ApplicationScoped
public class NotificationService {

    @Inject
    EmailClient emailClient; // Field injection
}

4. Qualificatori e disambiguazione di implementazioni multiple

Quando più bean implementano la stessa interfaccia, CDI lancia un’eccezione di dipendenza ambigua in fase di compilazione. Quarkus utilizza i Qualificatori per risolvere in modo sicuro le implementazioni dei bean target.

Definizione dei qualificatori personalizzati

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
    }
}

Implementazione di bean qualificati

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);
    }
}

Iniezione di fagioli qualificati

@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. Produttori e smaltitori: immissione di oggetti di terze parti

A volte è necessario inserire classi da librerie di terze parti (ad esempio, client SDK AWS, connessioni Redis, bean di configurazione personalizzati) che non sono annotate con @ApplicationScoped. Quarkus gestisce questa operazione utilizzando Metodi del produttore e Disposer.

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. Intercettori CDI: programmazione orientata agli aspetti (AOP)

Gli interceptor consentono di collegare aspetti trasversali (registrazione, audit trail, metriche, limitazione della velocità) ai metodi bean senza inquinare la logica aziendale.

Passaggio 1: definire il collegamento dell’intercettore

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 {
}

Passaggio 2: implementare la logica dell’interceptor

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);
        }
    }
}

Passaggio 3: annotare i target bean

@ApplicationScoped
public class AnalyticsService {

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

Poiché Quarkus genera proxy interceptor statici in fase di compilazione, l’overhead di chiamata del metodo è praticamente pari a zero rispetto ai proxy runtime CGLIB dinamici in Spring Boot.


7. Architettura guidata dagli eventi con eventi CDI

CDI fornisce la pubblicazione di eventi sincrona e asincrona incorporata per la comunicazione in-process disaccoppiata.

Pubblicazione e osservazione di eventi

// 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());
    }
}

Eventi del ciclo di vita dell’applicazione

Quarkus espone eventi di avvio e arresto per eseguire il codice di inizializzazione quando l’applicazione viene avviata o terminata:

@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. Ricerca bean programmatica con Instance<T> e Arc.container()

Quando le implementazioni di destinazione devono essere scelte dinamicamente in fase di runtime (o all’interno di istanze legacy non CDI), utilizzare la ricerca statica Instance<T> o Quarkus Arc.container().

@ApplicationScoped
public class DynamicPluginRegistry {

    @Inject
    Instance<PaymentProcessor> processors;

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

9. Confronto architettonico: Quarkus ArC vs Spring IoC vs Weld

Caratteristica Quarkus ArC Contenitore IoC primaverile Saldatura standard di Giacarta
Risoluzione delle dipendenze Tempo di costruzione (AOT) Riflessione in fase di esecuzione Riflessione in fase di esecuzione
Carico RAM Metaspace Estremamente basso (~0 MB per i metadati) Alta (cache classe proxy dinamica) Da moderato ad alto
Tempo di avvio Millisecondi (< 0,05 s) Secondi (2,5 s - 8 s) Secondi (1,5 s - 4 s)
Supporto nativo GraalVM Nativo fuori dagli schemi Richiede suggerimenti nativi primaverili Configurazione complessa
Potatura di fagioli inutilizzati Sfoltimento automatico in fase di compilazione Conservato in Classpath Conservato in Classpath
Iniezione di un singolo costruttore Implicito (nessuna annotazione necessaria) Supportato Richiede “@Inject”.

10. Test unitario dei bean CDI con @QuarkusTest e @InjectMock

Quarkus semplifica il test dei bean CDI con JUnit 5 e RestAssured. Utilizza @InjectMock per sostituire automaticamente i bean di dipendenza con i mock Mockito durante i test.

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. Conclusione

L’inserimento delle dipendenze in Quarkus con CDI (ArC) segna un cambiamento epocale nella progettazione Java aziendale. Trasferendo la scansione dei metadati dei bean, la generazione di proxy e la risoluzione del grafico di iniezione al momento della compilazione, Quarkus offre velocità di avvio supersoniche, impronta di memoria subatomica e supporto binario nativo GraalVM senza soluzione di continuità.

Che tu stia migrando da Spring Boot o creando microservizi nativi del cloud su Kubernetes, padroneggiare Quarkus CDI ti fornisce le prestazioni e la produttività degli sviluppatori necessarie per la moderna architettura software.

Ghaznix Ecosystem Products

Empower Your Digital Presence & Workflows

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