Belajar Quarkus - Dependency Injection & CDI
Episode 4 of 24

Belajar Quarkus - Dependency Injection & CDI

Episode ini membahas CDI di Quarkus secara menyeluruh: annotation @Inject dan scope bean, producer dan qualifier, alternative bean, lifecycle bean, serta penggunaan interceptor untuk memisahkan concern lintas aplikasi.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Pendahuluan

Di episode 3 kalian sudah menjalankan aplikasi Quarkus pertama. Sekarang saatnya memahami mesin yang menghubungkan seluruh komponen: CDI (Contexts and Dependency Injection). Quarkus memakai implementasi CDI bernama ArC yang diproses saat build time.

Dependency injection bukan sekadar pola praktis — ia adalah arsitektur inti aplikasi Quarkus. Bean service, repository, dan resource semuanya dihubungkan lewat CDI. Episode ini membahas @Inject, scope bean, producer, qualifier, alternative, lifecycle, dan interceptor secara mendalam.

Dasar CDI: @Inject dan Scope Bean

Bean dan Container ArC

Sebuah bean adalah class yang dikelola container CDI. Container membuat instance, mengelola siklus hidup, dan menyuntikkannya ke tempat yang membutuhkan. Di Quarkus, container ArC mengenali bean saat build time sehingga tidak ada overhead scanning saat runtime.

Scope yang Paling Umum

Scope menentukan berapa lama sebuah bean hidup:

  • @ApplicationScoped: satu instance per aplikasi. Paling umum untuk service stateless.
  • @Singleton: satu instance, tanpa proxy.
  • @RequestScoped: satu instance per request HTTP.
  • @Dependent: mengikuti scope pemiliknya.

Contoh bean dengan scope:

JavaBean service dengan scope
import jakarta.enterprise.context.ApplicationScoped;
 
@ApplicationScoped
public class GreetingService {
 
    public String sapa(String nama) {
        return "Halo, " + nama + "!";
    }
}

Menyuntikkan dengan @Inject

JavaMenggunakan @Inject
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
 
@Path("/api/greeting")
public class GreetingResource {
 
    @Inject
    GreetingService greetingService;
 
    @GET
    public String sapa() {
        return greetingService.sapa("Quarkus");
    }
}

Field @Inject GreetingService greetingService meminta container menyuntikkan instance GreetingService. Jalankan dengan ./mvnw quarkus:dev dan buka endpoint untuk melihat hasilnya.

Producer dan Qualifier

Producer Method

Kadang kalian perlu membuat bean dari objek yang tidak memiliki constructor kosong atau dari tipe pihak ketiga. Solusinya adalah producer method:

JavaProducer untuk objek eksternal
import jakarta.enterprise.inject.Produces;
import jakarta.enterprise.context.ApplicationScoped;
import java.time.Clock;
 
@ApplicationScoped
public class ClockProducer {
 
    @Produces
    Clock clock() {
        return Clock.systemUTC();
    }
}

Bean Clock sekarang bisa di-@Inject di mana saja, dan instance dibuat oleh method producer ini.

Qualifier untuk Disambiguasi

Jika ada lebih dari satu bean bertipe sama, gunakan qualifier. Qualifier adalah annotation custom:

JavaQualifier custom
import jakarta.inject.Qualifier;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
@Qualifier
@Retention(RUNTIME)
@Target({FIELD, PARAMETER, METHOD})
public @interface Premium {}

Lalu pasang qualifier ini di bean dan di titik injeksi. Contoh: @Premium GreetingService service akan memilih bean yang diberi anotasi @Premium.

Alternative Bean dan Lifecycle

Menandai Bean Alternatif

Bean alternative berguna untuk mengaktifkan implementasi berbeda — misalnya implementasi mock untuk testing:

JavaAlternative bean
import jakarta.annotation.Priority;
import jakarta.enterprise.inject.Alternative;
 
@Alternative
@Priority(1000)
public class GreetingServicePremium extends GreetingService {
    @Override
    public String sapa(String nama) {
        return "Halo, " + nama + "! Anda member premium.";
    }
}

Sebuah class bisa menandai dirinya sebagai @Alternative dan diaktifkan lewat @Priority. Saat dua bean mengklaim tipe yang sama, bean dengan @Priority lebih kecil yang menang. Seleksi alternative juga bisa diatur lewat quarkus.arc.selected-alternatives di application.properties.

Callback Lifecycle

Bean punya siklus hidup yang bisa di-hook: method @PostConstruct dijalankan setelah dependency ter-inject, dan @PreDestroy dijalankan sebelum bean dimusnahkan — tempat yang tepat untuk membuka dan menutup resource.

Interceptor

Interceptor memisahkan concern lintas-potong seperti logging, timing, atau transaksi dari logika bisnis. Quarkus menyediakan interceptor bawaan seperti @Transactional (episode 10). Kalian juga bisa membuat interceptor sendiri memakai interceptor binding annotation:

JavaInterceptor binding
import jakarta.interceptor.InterceptorBinding;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
@InterceptorBinding
@Retention(RUNTIME)
@Target({METHOD, TYPE})
public @interface Logged {}

Kombinasi @ApplicationScoped dan interceptor memungkinkan kalian menambah behavior pada method tanpa mengubah logika intinya. Untuk aplikasi dengan banyak library pihak ketiga, kontrol discovery bean ArC lewat quarkus.arc.exclude-types dan quarkus.arc.unremovable-types. Best practice: gunakan @ApplicationScoped sebagai default untuk service stateless, @RequestScoped untuk state per request, dan hindari @Singleton kecuali benar-benar butuh bean tanpa proxy.

Penutup

Episode 4 memberi kalian kontrol penuh atas dependency injection di Quarkus: @Inject dan scope bean, producer method untuk tipe eksternal, qualifier untuk disambiguasi, alternative bean untuk seleksi implementasi, callback lifecycle, interceptor untuk concern lintas-potong, serta konfigurasi discovery bean ArC.

Inti yang harus dibawa pulang:

  • ArC adalah container CDI Quarkus yang bekerja saat build time.
  • @ApplicationScoped adalah default terbaik untuk service stateless.
  • Producer method membuat bean dari tipe tanpa constructor kosong.
  • Qualifier membedakan beberapa bean bertipe sama.
  • @Alternative dengan @Priority memilih implementasi aktif.
  • @PostConstruct dan @PreDestroy mengelola lifecycle bean.
  • Interceptor binding memisahkan concern lintas-potong dari logika bisnis.

Di episode 5 selanjutnya kita akan membangun REST API dengan RESTEasy Reactive — dari mapping @GET, @POST, @PUT, @DELETE, dan @Path, binding body dan validasi, hingga error handling dengan custom exception mapper.

Belajar Quarkus - Dependency Injection & CDI | Belajar Quarkus