Spring: Selalu Gunakan Constructor Injection!

Dian Aditya
Catatan Programmer
Published in
2 min readSep 1, 2017

Saya adalah pengguna field injection sejak pertama kali @Autowired diperkenalkan pada Spring versi 2.5. Kesan pertama saat menggunakannya pada field adalah “wow, magic”, saya tidak perlu membuat public setter ataupun constructor untuk dapat meng-inject dependency pada bean yang saya buat. Sampai pada sekitar akhir tahun lalu saya menyadari bahwa menggunakan field injection bukanlah ide yang cukup baik.

Mari kita bahas lebih dalam mengapa.

Masalah Pertama, Terlalu Mudah (What?)

Alasan utama menggunakan field injection adalah karena sangat sederhana, menghemat baris kode, dan tidak mengotori constructor. Saya yakin saya bukan satu-satunya yang berpikir demikian.

Indah bukan? Cukup menambahkan @Autowired dan kedua instance dari service diatas secara otomatis telah ter-inject kedalam ServiceAImpl. Jika saya ingin menambahkan dependency lain saya cukup mendeklarasikan field baru. Saya dapat meng-inject sebanyak yang saya mau tanpa membuat class saya terlihat kotor. Bandingkan dengan kode berikut:

Dengan 3 dependency saja class saya sudah kurang enak dipandang. Bagaimana jika saya ingin menambah beberapa dependency lagi? Constructor saya akan terlihat sangat berantakan.

Hingga pada suatu ketika saya menyadari service class saya memiliki banyak sekali method. Jawabannya tidak lain karena class service tersebut memikul terlalu banyak tanggung jawab yang mana menciderai prinsip Single Responsibility. Kemudahan menggunakan field injection membuat saya terlalu mudah menambahkan dependency. Dengan menggunakan constructor injection, saya akan lebih cepat menyadari parameter yang terlalu banyak menandakan ada yang salah dengan desain yang dibuat.

Masalah Kedua, Cyclic Dependency

Cyclic dependency adalah saat dua bean atau lebih saling membutuhkan satu sama lain untuk dapat digunakan. Sebagai contoh ServiceA bergantung padaServiceB, ServiceB bergantung pada ServiceC, dan ServiceC bergantung pada ServiceA. Cyclic dependency sering terjadi ketika jumlah service kian bertambah banyak dan membuat kode saling bergantung, yang pada akhirnya mengabaikan modularitas.

Pada contoh diatas walaupun struktur dependency berputar, spring dapat mengatasinya karena setiap bean diinisialisasi terlebih dahulu dan injection dilakukan setelahnya. Berikut potongan output ketika spring container dijalankan:

ServiceA                        : Initializing ServiceA
ServiceB : Initializing ServiceB
ServiceC : Initializing ServiceC
ServiceC : ServiceA injected
ServiceB : ServiceC injected
ServiceA : ServiceB injected

Sekarang mari kita ubah setiap class diatas menggunakan constructor injection.

Tidak ada yang berbeda dari struktur dependency dengan kode sebelumnya selain perubahan field injection menjadi constructor injection. Namun ketika container spring dijalankan terjadi error sebagai berikut:

***************************
APPLICATION FAILED TO START
***************************
Description:The dependencies of some of the beans in the application context form a cycle:┌─────┐
| serviceA defined in file [/.../ServiceA.class]
↑ ↓
| serviceB defined in file [/.../ServiceB.class]
↑ ↓
| serviceC defined in file [/.../ServiceC.class]
└─────┘

Kesimpulan

Constructor injection membuat kita lebih sadar terhadap class yang memiliki dependency terlalu banyak, constructor parameter yang terlalu banyak merefleksikan sebuah class memiliki tanggung jawab yang terlalu besar.

Cyclic dependency dapat ditiadakan dengan menggunakan constructor injection, karena container spring tidak akan dapat dijalankan jika hal tersebut terjadi.

--

--