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.

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.
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 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:
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class GreetingService {
public String sapa(String nama) {
return "Halo, " + nama + "!";
}
}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.
Kadang kalian perlu membuat bean dari objek yang tidak memiliki constructor kosong atau dari tipe pihak ketiga. Solusinya adalah producer method:
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.
Jika ada lebih dari satu bean bertipe sama, gunakan qualifier. Qualifier adalah annotation 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.
Bean alternative berguna untuk mengaktifkan implementasi berbeda — misalnya implementasi mock untuk testing:
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.
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 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:
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.
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:
@ApplicationScoped adalah default terbaik untuk service stateless.@Alternative dengan @Priority memilih implementasi aktif.@PostConstruct dan @PreDestroy mengelola lifecycle bean.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.