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

Injeção de dependência no Quarkus com CDI: ArC, escopos, qualificadores e injeção em tempo de construção

Injeção de dependência no Quarkus com CDI: ArC, escopos, qualificadores e injeção em tempo de construção

A injeção de dependência (DI) é a base do desenvolvimento de aplicativos Java corporativos modernos. Durante anos, os desenvolvedores Java confiaram no Spring IoC (Inversão de Controle) ou em contêineres Jakarta EE CDI (Contextos e Injeção de Dependência) padrão, como Hibernate Weld ou OpenWebBeans para desacoplar ciclos de vida de componentes, gerenciar dependências de serviço e injetar propriedades de configuração.

No entanto, à medida que os aplicativos corporativos migraram para microsserviços nativos da nuvem, a conteinerização com Docker e Kubernetes e modelos de execução sem servidor (AWS Lambda, Knative), os contêineres tradicionais de injeção de dependência expuseram uma falha crítica: reflexão pesada no tempo de execução e alta sobrecarga de memória.

Entra em cena o Quarkus e seu mecanismo CDI leve, ArC. Desenvolvido desde o início para compilação nativa do Kubernetes e GraalVM, o Quarkus redefine a injeção de dependência mudando a reflexão, a varredura do caminho de classe e a fiação do gráfico de bean do tempo de execução do aplicativo para o tempo de construção.

Neste guia aprofundado, exploraremos Injeção de dependência no Quarkus com CDI, abordando a mecânica interna do contêiner ArC, escopos CDI, qualificadores personalizados, produtores, interceptadores, observadores orientados a eventos, pesquisas programáticas e práticas recomendadas para escrever aplicativos Java nativos da nuvem de alto desempenho.


1. Compreendendo o ArC: Quarkus Build-Time CDI Engine

Os contêineres Jakarta EE CDI padrão operam inteiramente na inicialização do aplicativo. Quando um aplicativo legado é inicializado, o contêiner CDI verifica cada JAR no caminho de classe, analisa metadados, inspeciona anotações usando Java Reflection (java.lang.reflect), gera proxies dinâmicos na memória e constrói o gráfico de dependência. Essa reflexão dinâmica consome RAM significativa (Metaspace) e aumenta o tempo de inicialização a frio.

O Quarkus resolve esse gargalo com ArC (uma implementação de CDI em tempo de construção baseada nas especificações Jakarta CDI 4.0).

CDI de tempo de execução tradicional com reflexão vs Quarkus ArC Build-Time Wiring

Como o ArC difere do CDI padrão:

  1. Aumento no tempo de compilação: Durante mvn package ou ./gradlew build, o ArC verifica todos os beans do aplicativo, valida os pontos de injeção e pré-constrói o gráfico de dependência.
  2. Geração de bytecode Ahead-Of-Time (AOT): ArC gera bytecode Java direto para instanciação, proxies de bean e interceptadores antecipadamente, eliminando reflexão em tempo de execução.
  3. Remoção de Bean Não Utilizado (Eliminação de Código Morto): Qualquer bean CDI que nunca seja injetado ou referenciado dinamicamente é identificado durante a compilação e automaticamente removido do binário final, reduzindo o consumo de memória.
  4. Otimização de imagem nativa GraalVM: Como a fiação do bean é resolvida no momento da construção, os aplicativos Quarkus são compilados perfeitamente em executáveis ​​nativos com zero dicas de configuração de reflexão.

2. Escopos do Bean CDI e gerenciamento do ciclo de vida

No CDI, um Escopo determina o ciclo de vida e a visibilidade de uma instância de bean. Quarkus ArC suporta escopos Jakarta CDI padrão com otimizações de tempo de construção.

Diagrama de escopos e ciclos de vida do Quarkus CDI Bean

Escopos CDI primários no Quarkus:

Anotação de escopo Ciclo de vida e comportamento Criação de proxy Melhor caso de uso
@ApplicationScoped Instância única por aplicativo. Inicialização lenta na primeira invocação. Estado compartilhado por todos os threads. Proxy do cliente gerado Serviços, repositórios e configurações sem estado.
@RequestScoped Instância vinculada a uma única solicitação HTTP ou invocação de evento. Destruído quando a solicitação for concluída. Proxy do cliente gerado Sessões de usuário, solicitação de dados de contexto, tokens de segurança.
@Dependente Escopo padrão se nenhum for especificado. Instância vinculada diretamente ao tempo de vida do bean de injeção. Sem proxy (instância direta) Classes utilitárias auxiliares, detentores de estado efêmero.
@Singleton Instância única por aplicativo. Instanciado ansiosamente ou preguiçosamente. Nenhum wrapper de proxy do cliente. Sem proxy (instância direta) Singletons de alto desempenho onde a sobrecarga do proxy do cliente deve ser evitada.
@Irremovível Anotação de marcador instruindo o ArC a não remover o bean durante a otimização do tempo de construção. Depende do escopo Destinos de pesquisa dinâmicos ou beans referenciados por reflexão.

Exemplo de código: declarando beans com escopo

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. Padrões de injeção de dependência no Quarkus

O Quarkus suporta vários estilos de injeção usando a anotação padrão jakarta.inject.Inject.

A. Injeção de construtor (melhores práticas recomendadas)

A injeção de construtor garante imutabilidade (campos final), simplifica o teste de unidade sem exigir contêineres CDI e torna as dependências explícitas.

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

Dica profissional: No Quarkus ArC, se um bean tiver apenas um construtor, você pode omitir totalmente a anotação @Inject. ArC o reconhece automaticamente como o construtor de injeção.

B. Injeção de campo

A injeção de campo é concisa, mas torna os campos mutáveis ​​e complica os testes de unidade independentes.

@ApplicationScoped
public class NotificationService {

    @Inject
    EmailClient emailClient; // Field injection
}

4. Qualificadores e desambiguação de múltiplas implementações

Quando vários beans implementam a mesma interface, o CDI lança uma exceção de dependência ambígua no momento da construção. O Quarkus usa Qualificadores para resolver implementações de bean alvo com segurança.

Definindo qualificadores personalizados

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

Implementando Beans Qualificados

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

Injetando Feijões Qualificados

@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. Produtores e descartadores: injeção de objetos de terceiros

Às vezes, você precisa injetar classes de bibliotecas de terceiros (por exemplo, clientes AWS SDK, conexões Redis, beans de configuração personalizados) que não são anotadas com @ApplicationScoped. O Quarkus lida com isso usando Producer Methods e Disposers.

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. Interceptores CDI: Programação Orientada a Aspectos (AOP)

Os interceptadores permitem que preocupações transversais (registro, trilhas de auditoria, métricas, limitação de taxa) sejam anexadas aos métodos de bean sem poluir a lógica de negócios.

Etapa 1: Definir a vinculação do interceptor

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

Etapa 2: Implementar a lógica do interceptador

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

Etapa 3: anotar os beans alvo

@ApplicationScoped
public class AnalyticsService {

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

Como o Quarkus gera proxies de interceptor estático em tempo de construção, a sobrecarga de invocação de método é praticamente zero em comparação com proxies de tempo de execução CGLIB dinâmicos no Spring Boot.


7. Arquitetura Orientada a Eventos com Eventos CDI

O CDI fornece publicação integrada de eventos síncronos e assíncronos para comunicação desacoplada no processo.

Publicação e observação de eventos

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

Eventos do ciclo de vida do aplicativo

O Quarkus expõe eventos de inicialização e desligamento para executar o código de inicialização quando o aplicativo é iniciado ou encerrado:

@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. Pesquisa programática de Bean com Instance<T> e Arc.container()

Quando as implementações de destino devem ser escolhidas dinamicamente em tempo de execução (ou dentro de instâncias legadas não CDI), use a pesquisa estática Instance<T> ou Quarkus Arc.container().

@ApplicationScoped
public class DynamicPluginRegistry {

    @Inject
    Instance<PaymentProcessor> processors;

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

9. Comparação arquitetônica: Quarkus ArC vs Spring IoC vs Weld

Recurso Quarkus Arc Contêiner Spring IoC Solda Padrão Jacarta
Resolução de Dependências Tempo de construção (AOT) Reflexão em tempo de execução Reflexão em tempo de execução
Sobrecarga de RAM do metaespaço Extremamente baixo (~0 MB para metadados) Alto (cache de classe de proxy dinâmico) Moderado a alto
Hora de inicialização Milissegundos (<0,05s) Segundos (2,5s - 8s) Segundos (1,5s - 4s)
Suporte nativo GraalVM Nativo pronto para uso Requer dicas nativas do Spring Configuração complexa
Poda de feijão não utilizada Poda automática em tempo de construção Mantido no caminho de classe Mantido no caminho de classe
Injeção de construtor único Implícito (nenhuma anotação necessária) Suportado Requer @Inject

10. Teste unitário de CDI Beans com @QuarkusTest e @InjectMock

O Quarkus facilita o teste de beans CDI com JUnit 5 e RestAssured. Use @InjectMock para substituir beans de dependência por simulações Mockito automaticamente durante o teste.

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. Conclusão

A injeção de dependência no Quarkus com CDI (ArC) marca uma mudança monumental no design Java corporativo. Ao transferir a varredura de metadados de bean, a geração de proxy e a resolução do gráfico de injeção para o tempo de compilação, o Quarkus oferece velocidades de inicialização supersônicas, pegadas de memória subatômica e suporte binário nativo do GraalVM.

Esteja você migrando do Spring Boot ou construindo microsserviços nativos da nuvem no Kubernetes, dominar o Quarkus CDI equipa você com o desempenho e a produtividade do desenvolvedor necessários para a arquitetura de software moderna.

Ghaznix Ecosystem Products

Empower Your Digital Presence & Workflows

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