CDI ile Quarkus'ta Bağımlılık Enjeksiyonu: ArC, Kapsamlar, Niteleyiciler ve Oluşturma Zamanı Enjeksiyonu
Bağımlılık Enjeksiyonu (DI), modern kurumsal Java uygulaması geliştirmenin temelidir. Yıllar boyunca Java geliştiricileri, bileşen yaşam döngülerini ayırmak, hizmet bağımlılıklarını yönetmek ve yapılandırma özelliklerini eklemek için Spring IoC (Kontrolün Tersine Dönüştürülmesi) veya Hibernate Weld veya OpenWebBeans gibi standart Jakarta EE CDI (Bağlamlar ve Bağlamlar Enjeksiyonu) kapsayıcılarına güvendi.
Ancak kurumsal uygulamalar bulutta yerel mikro hizmetlere, Docker ve Kubernetes ile konteynerleştirmeye ve sunucusuz yürütme modellerine (AWS Lambda, Knative) doğru geçiş yaptıkça, geleneksel bağımlılık enjeksiyon konteynerleri kritik bir kusuru ortaya çıkardı: yoğun çalışma zamanı yansıması ve yüksek bellek yükü.
Quarkus ve hafif CDI motoru ArC‘ye girin. Kubernetes ve GraalVM yerel derlemesi için sıfırdan oluşturulan Quarkus, yansımayı, sınıf yolu taramayı ve fasulye grafiği kablolamasını uygulama çalışma zamanından derleme zamanına kaydırarak bağımlılık enjeksiyonunu yeniden tanımlıyor.
Bu ayrıntılı kılavuzda ArC kapsayıcısının iç mekaniğini, CDI kapsamlarını, özel niteleyicileri, üreticileri, önleyicileri, olay odaklı gözlemcileri, programatik aramaları ve yüksek performanslı bulutta yerel Java uygulamaları yazmaya yönelik en iyi uygulamaları kapsayan CDI ile Quarkus’ta Bağımlılık Enjeksiyonunu keşfedeceğiz.
1. ArC’yi Anlamak: Quarkus Yapım Zamanı CDI Motoru
Standart Jakarta EE CDI kapsayıcıları tamamen uygulama başlangıcında çalışır. Eski bir uygulama önyüklendiğinde, CDI kapsayıcısı sınıf yolundaki her JAR’ı tarar, meta verileri ayrıştırır, Java Reflection (java.lang.reflect) kullanarak ek açıklamaları inceler, bellekte dinamik proxy’ler oluşturur ve bağımlılık grafiğini oluşturur. Bu dinamik yansıma, önemli miktarda RAM (Metaspace) tüketir ve soğuk başlatma önyükleme süresini artırır.
Quarkus bu darboğazı ArC (Jakarta CDI 4.0 özelliklerini temel alan bir yapım aşamasında CDI uygulaması) ile çözmektedir.
ArC’nin Standart CDI’dan Farkı:
- Derleme Zamanını Artırma:
mvn packageveya./gradlew buildsırasında ArC, tüm uygulama çekirdeklerini tarar, enjeksiyon noktalarını doğrular ve bağımlılık grafiğini önceden oluşturur. - Zamandan Önce (AOT) Bayt Kodu Oluşturma: ArC, örnekleme, fasulye proxy’leri ve önleyiciler için doğrudan Java bayt kodu oluşturarak çalışma zamanında yansımayı ortadan kaldırır.
- Kullanılmayan Fasulye Budama (Ölü Kodun Ortadan Kaldırılması): Hiçbir zaman enjekte edilmeyen veya dinamik olarak referans verilmeyen herhangi bir CDI çekirdeği, derleme sırasında tanımlanır ve otomatik olarak son ikili dosyadan çıkarılarak bellek ayak izi azaltılır.
- GraalVM Yerel Görüntü Optimizasyonu: Fasulye kablolaması derleme sırasında çözüldüğü için Quarkus uygulamaları, sıfır yansıma yapılandırma ipuçlarıyla yerel yürütülebilir dosyalar halinde sorunsuz bir şekilde derlenir.
2. CDI Bean Kapsamları ve Yaşam Döngüsü Yönetimi
CDI’da Kapsam, bir fasulye örneğinin yaşam döngüsünü ve görünürlüğünü belirler. Quarkus ArC, derleme zamanı optimizasyonlarıyla standart Jakarta CDI kapsamlarını destekler.
Quarkus’taki Birincil CDI Kapsamları:
| Kapsam Açıklaması | Yaşam Döngüsü ve Davranış | Proxy Oluşturma | En İyi Kullanım Durumu |
|---|---|---|---|
@ApplicationScoped |
Uygulama başına tek örnek. İlk çalıştırmada tembel başlatma. Durum tüm başlıklarda paylaşıldı. | İstemci Proxy’si Oluşturuldu | Durum bilgisi olmayan hizmetler, depolar, yapılandırmalar. |
@RequestScoped |
Örnek, tek bir HTTP isteğine veya olay çağrısına bağlıdır. İstek tamamlandığında yok edilir. | İstemci Proxy’si Oluşturuldu | Kullanıcı oturumları, istek bağlam verileri, güvenlik belirteçleri. |
@Bağımlı |
Hiçbiri belirtilmemişse varsayılan kapsam. Örnek doğrudan enjekte edilen çekirdeğin ömrüne bağlıdır. | Proxy Yok (Doğrudan Örnek) | Yardımcı fayda sınıfları, geçici devlet sahipleri. |
@Singleton |
Uygulama başına tek örnek. Hevesle veya tembelce örneklendi. İstemci proxy sarmalayıcısı yok. | Proxy Yok (Doğrudan Örnek) | İstemci proxy ek yükünden kaçınılması gereken yüksek performanslı singletonlar. |
@Kaldırılamaz |
ArC’ye derleme zamanı optimizasyonu sırasında çekirdeği çıkarmaması talimatını veren işaretleyici açıklama. | Kapsama bağlıdır | Dinamik arama hedefleri veya yansıma referanslı fasulyeler. |
Kod Örneği: Kapsamlı Fasulyelerin Bildirilmesi
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’ta Bağımlılık Enjeksiyon Kalıpları
Quarkus, standart jakarta.inject.Inject ek açıklamasını kullanarak birden fazla enjeksiyon stilini destekler.
A. Yapıcı Enjeksiyonu (Önerilen En İyi Uygulama)
Yapıcı enjeksiyonu değişmezliği garanti eder (final alanları), CDI kapsayıcılarına ihtiyaç duymadan birim testini basitleştirir ve bağımlılıkları açıkça ortaya koyar.
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);
}
}
Profesyonel İpucu: Quarkus ArC’de, eğer bir fasulyenin yalnızca tek yapıcısı varsa,
@Injectek açıklamasını tamamen atlayabilirsiniz. ArC onu otomatik olarak enjeksiyon yapıcısı olarak tanır.
B. Alan Enjeksiyonu
Alan enjeksiyonu kısa ve özdür ancak alanları değiştirilebilir hale getirir ve bağımsız birim testini karmaşık hale getirir.
@ApplicationScoped
public class NotificationService {
@Inject
EmailClient emailClient; // Field injection
}
4. Niteleyiciler ve Çoklu Uygulamaların Belirsizliğinin Ortaya Çıkarılması
Birden fazla fasulye aynı arayüzü uyguladığında CDI, derleme zamanında belirsiz bir bağımlılık istisnası oluşturur. Quarkus, hedef fasulye uygulamalarını güvenli bir şekilde çözmek için Niteleyicileri kullanır.
Özel Niteleyicileri Tanımlama
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
}
}
Nitelikli Fasulyelerin Uygulanması
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);
}
}
Nitelikli Fasulyelerin Enjekte Edilmesi
@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. Üreticiler ve İmha Ediciler: Üçüncü Taraf Nesnelerinin Enjekte Edilmesi
Bazen üçüncü taraf kitaplıklardan (örneğin, AWS SDK istemcileri, Redis bağlantıları, özel yapılandırma çekirdekleri) @ApplicationScoped ile açıklanmayan sınıfları enjekte etmeniz gerekir. Quarkus bunu Üretici Yöntemleri ve Atıklayıcılar kullanarak hallediyor.
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 Durdurucuları: Yön Odaklı Programlama (AOP)
Durdurucular, iş mantığını kirletmeden, kesişen endişelerin (günlüğe kaydetme, denetim izleri, ölçümler, hız sınırlama) fasulye yöntemlerine eklenmesine olanak tanır.
Adım 1: Durdurucu Bağlamayı Tanımlayın
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 {
}
Adım 2: Durdurucu Mantığını Uygulama
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);
}
}
}
Adım 3: Hedef Fasulyelere Açıklama Ekleyin
@ApplicationScoped
public class AnalyticsService {
@LogExecutionTime
public void computeAggregates() {
// Business computation
}
}
Quarkus, derleme sırasında statik önleyici proxy’ler oluşturduğundan, yöntem çağırma yükü, Spring Boot’taki dinamik CGLIB çalışma zamanı proxy’lerine kıyasla neredeyse sıfırdır.
7. CDI Etkinlikleri ile Olay Odaklı Mimari
CDI, ayrıştırılmış süreç içi iletişim için yerleşik eşzamanlı ve eşzamansız olay yayımlama sağlar.
Etkinlikleri Yayınlama ve Gözlemleme
// 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());
}
}
Uygulama Yaşam Döngüsü Olayları
Quarkus, uygulama başlatıldığında veya sonlandırıldığında başlatma kodunu yürütmek için başlatma ve kapatma olaylarını kullanıma sunar:
@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> ve Arc.container() ile Programlı Bean Araması
Hedef uygulamaların çalışma zamanında (veya eski CDI olmayan örnekler içinde) dinamik olarak seçilmesi gerektiğinde, Instance<T> veya Quarkus Arc.container() statik aramayı kullanın.
@ApplicationScoped
public class DynamicPluginRegistry {
@Inject
Instance<PaymentProcessor> processors;
public void executeAll(double amount) {
for (PaymentProcessor processor : processors) {
processor.process(amount);
}
}
}
9. Mimari Karşılaştırma: Quarkus ArC, Spring IoC ve Weld
| Özellik | Kuark ArC | Bahar IoC Kabı | Standart Jakarta Kaynağı |
|---|---|---|---|
| Bağımlılık Çözümü | Yapım Süresi (AOT) | Çalışma Zamanı Yansıması | Çalışma Zamanı Yansıması |
| Metaspace RAM Yükü | Son Derece Düşük (meta veriler için ~0 MB) | Yüksek (Dinamik Proxy Sınıfı Önbelleği) | Orta ila Yüksek |
| Başlangıç Zamanı | Milisaniye (< 0,05s) | Saniye (2,5s - 8s) | Saniye (1,5s - 4s) |
| GraalVM Yerel Desteği | Yerli Kullanıma hazır | Bahar Yerel İpuçları gerektirir | Karmaşık konfigürasyon |
| Kullanılmayan Fasulye Budama | Otomatik Yapım Zamanı Budama | Classpath’ta tutuldu | Classpath’ta tutuldu |
| Tek Yapıcı Enjeksiyonu | Örtülü (Ek açıklamaya gerek yok) | Destekleniyor | ‘@Inject’ gerektirir |
10. @QuarkusTest ve @InjectMock ile CDI Fasulyelerinin Birim Testi
Quarkus, JUnit 5 ve RestAssured ile CDI çekirdeklerinin test edilmesini sorunsuz hale getiriyor. Bağımlılık fasulyelerini test sırasında otomatik olarak Mockito alaylarıyla değiştirmek için @InjectMock kullanın.
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. Sonuç
Quarkus’ta CDI (ArC) ile Dependency Injection, kurumsal Java tasarımında muazzam bir değişime işaret ediyor. Quarkus, fasulye meta veri taramasını, proxy oluşturmayı ve enjeksiyon grafiği çözünürlüğünü derleme zamanına aktararak süpersonik başlatma hızları, atomik bellek ayak izleri ve kesintisiz GraalVM yerel ikili desteği sunar.
İster Spring Boot’tan geçiş yapın, ister Kubernetes’te sıfırdan bulutta yerel mikro hizmetler oluşturuyor olun, Quarkus CDI’da uzmanlaşmak sizi modern yazılım mimarisi için gereken performans ve geliştirici üretkenliğiyle donatır.
Tags
Empower Your Digital Presence & Workflows
Explore top-tier tools built by Ghaznix to streamline your links, surveys, and brand growth.