Saya mengajukan pertanyaan umum Musim Semi: Auto-cast Spring Beans dan meminta banyak orang menjawab bahwa menelepon Spring ApplicationContext.getBean()
harus dihindari sebanyak mungkin. Mengapa demikian?
Bagaimana lagi saya bisa mendapatkan akses ke kacang yang saya konfigurasikan Spring untuk membuat?
Saya menggunakan Spring dalam aplikasi non-web dan telah merencanakan mengakses ApplicationContext
objek bersama seperti yang dijelaskan oleh LiorH .
Amandemen
Saya menerima jawaban di bawah ini, tetapi inilah pilihan lain oleh Martin Fowler yang membahas manfaat Dependency Injection vs. menggunakan Service Locator (yang pada dasarnya sama dengan memanggil dibungkus ApplicationContext.getBean()
).
Sebagian, Fowler menyatakan, " Dengan locator layanan, kelas aplikasi memintanya [layanan] secara eksplisit melalui pesan ke locator. Dengan injeksi tidak ada permintaan eksplisit, layanan muncul di kelas aplikasi - maka inversi kontrol. Inversion of control adalah fitur umum dari frameworks, tapi itu adalah sesuatu yang mahal, cenderung sulit untuk dipahami dan mengarah ke masalah ketika Anda mencoba untuk debug. Jadi secara keseluruhan saya lebih suka menghindarinya [Inversion of Control ] kecuali saya membutuhkannya. Ini bukan untuk mengatakan itu adalah hal yang buruk, hanya saja saya pikir itu perlu membenarkan dirinya atas alternatif yang lebih mudah. "
new MyOtherClass()
objek? Aku tahu tentang @ Autowired, tapi aku hanya pernah digunakan pada bidang dan istirahat padanew MyOtherClass()
..applicationContext.getBean
bukan injeksi ketergantungan: ini mengakses kerangka kerja secara langsung, menggunakannya sebagai pencari lokasi layanan .Provider<Foo>
alih - alih aFoo
dan memanggilprovider.get()
setiap kali Anda membutuhkan contoh baru. Tidak ada referensi ke wadah itu sendiri, dan Anda dapat dengan mudah membuatProvider
untuk pengujian.Alasan untuk memilih Penentu Lokasi Layanan daripada Inversion of Control (IoC) adalah:
Service Locator jauh, lebih mudah bagi orang lain untuk mengikuti kode Anda. IoC 'ajaib' tetapi programmer pemeliharaan harus memahami konfigurasi Spring yang berbelit-belit dan semua lokasi yang beragam untuk mengetahui bagaimana Anda menghubungkan kabel ke objek Anda.
IoC sangat buruk untuk debugging masalah konfigurasi. Di kelas aplikasi tertentu, aplikasi tidak akan mulai ketika salah konfigurasi dan Anda mungkin tidak mendapatkan kesempatan untuk menelusuri apa yang terjadi dengan debugger.
IoC terutama berbasis XML (Anotasi meningkatkan banyak hal tetapi masih ada banyak XML di luar sana). Itu berarti pengembang tidak dapat bekerja pada program Anda kecuali mereka tahu semua tag ajaib yang ditentukan oleh Spring. Tidak cukup baik untuk mengenal Jawa lagi. Hal ini menghambat programmer yang kurang berpengalaman (mis. Sebenarnya desain yang buruk untuk menggunakan solusi yang lebih rumit ketika solusi yang lebih sederhana, seperti Service Locator, akan memenuhi persyaratan yang sama). Plus, dukungan untuk mendiagnosis masalah XML jauh lebih lemah daripada dukungan untuk masalah Java.
Injeksi ketergantungan lebih cocok untuk program yang lebih besar. Sebagian besar waktu kompleksitas tambahan tidak sepadan.
Seringkali Spring digunakan jika Anda "mungkin ingin mengubah implementasinya nanti". Ada cara lain untuk mencapai ini tanpa kerumitan Spring IoC.
Untuk aplikasi web (Java EE WARs) konteks Spring secara efektif terikat pada waktu kompilasi (kecuali jika Anda ingin operator untuk mengitari konteks dalam perang yang meledak). Anda dapat membuat Spring menggunakan file properti, tetapi dengan servlets file properti harus berada di lokasi yang ditentukan sebelumnya, yang berarti Anda tidak dapat menggunakan beberapa servlet dari waktu yang sama di kotak yang sama. Anda dapat menggunakan Spring dengan JNDI untuk mengubah properti pada waktu permulaan servlet, tetapi jika Anda menggunakan JNDI untuk parameter yang dapat dimodifikasi administrator, kebutuhan untuk Spring itu sendiri berkurang (karena JNDI secara efektif adalah Service Locator).
Dengan Spring Anda dapat kehilangan Kontrol program jika Spring mengirim ke metode Anda. Ini nyaman dan berfungsi untuk banyak jenis aplikasi, tetapi tidak semua. Anda mungkin perlu mengontrol aliran program saat Anda perlu membuat tugas (utas dll) selama inisialisasi atau membutuhkan sumber daya yang dapat dimodifikasi yang tidak diketahui Spring saat konten terikat ke WAR Anda.
Musim semi sangat baik untuk manajemen transaksi dan memiliki beberapa keunggulan. Hanya saja IoC dapat melakukan rekayasa berlebihan dalam banyak situasi dan memperkenalkan kompleksitas yang tidak beralasan bagi para pengelola. Jangan menggunakan IoC secara otomatis tanpa memikirkan cara untuk tidak menggunakannya terlebih dahulu.
sumber
Memang benar bahwa menyertakan kelas dalam application-context.xml menghindari kebutuhan untuk menggunakan getBean. Namun, bahkan itu sebenarnya tidak perlu. Jika Anda menulis aplikasi mandiri dan Anda TIDAK ingin memasukkan kelas driver Anda di application-context.xml, Anda dapat menggunakan kode berikut untuk memiliki Spring autowire dependensi driver:
Saya perlu melakukan ini beberapa kali ketika saya memiliki semacam kelas mandiri yang perlu menggunakan beberapa aspek dari aplikasi saya (misalnya untuk pengujian) tetapi saya tidak ingin memasukkannya ke dalam konteks aplikasi karena tidak sebenarnya bagian dari aplikasi. Perhatikan juga bahwa ini menghindari kebutuhan untuk mencari kacang menggunakan nama String, yang saya selalu anggap jelek.
sumber
@Autowired
anotasi juga.Salah satu manfaat paling keren dari menggunakan sesuatu seperti Spring adalah Anda tidak harus menyatukan benda-benda Anda. Kepala Zeus terbelah terbuka dan kelas Anda muncul, sepenuhnya terbentuk dengan semua dependensinya dibuat dan terhubung, sesuai kebutuhan. Ajaib dan fantastis.
Semakin banyak Anda mengatakan
ClassINeed classINeed = (ClassINeed)ApplicationContext.getBean("classINeed");
, semakin sedikit sihir yang Anda dapatkan. Lebih sedikit kode hampir selalu lebih baik. Jika kelas Anda benar-benar membutuhkan kacang ClassINeed, mengapa Anda tidak langsung memasukkannya?Yang mengatakan, sesuatu jelas perlu membuat objek pertama. Tidak ada yang salah dengan metode utama Anda mendapatkan satu atau dua kacang melalui getBean (), tetapi Anda harus menghindarinya karena setiap kali Anda menggunakannya, Anda tidak benar-benar menggunakan semua keajaiban Spring.
sumber
Motivasinya adalah untuk menulis kode yang tidak bergantung secara eksplisit pada Spring. Dengan begitu, jika Anda memilih untuk mengganti wadah, Anda tidak perlu menulis ulang kode apa pun.
Pikirkan wadah sebagai sesuatu yang tidak terlihat oleh kode Anda, secara ajaib memenuhi kebutuhannya, tanpa diminta.
Injeksi ketergantungan adalah titik tandingan untuk pola "pencari lokasi". Jika Anda akan mencari dependensi berdasarkan nama, Anda sebaiknya menyingkirkan wadah DI dan menggunakan sesuatu seperti JNDI.
sumber
Menggunakan
@Autowired
atauApplicationContext.getBean()
benar-benar hal yang sama. Dalam kedua cara Anda mendapatkan kacang yang dikonfigurasi dalam konteks Anda dan dalam kedua cara kode Anda tergantung pada pegas. Satu-satunya hal yang harus Anda hindari adalah instantiating ApplicationContext Anda. Lakukan ini hanya sekali! Dengan kata lain, garis sepertiseharusnya hanya digunakan sekali dalam aplikasi Anda.
sumber
Idenya adalah bahwa Anda bergantung pada injeksi ketergantungan ( inversi kontrol , atau IOC). Artinya, komponen Anda dikonfigurasikan dengan komponen yang mereka butuhkan. Ketergantungan ini disuntikkan (melalui konstruktor atau setter) - Anda tidak mendapatkannya sendiri.
ApplicationContext.getBean()
mengharuskan Anda memberi nama kacang secara eksplisit di dalam komponen Anda. Sebagai gantinya, dengan menggunakan IoC, konfigurasi Anda dapat menentukan komponen apa yang akan digunakan.Ini memungkinkan Anda untuk memasang kembali aplikasi Anda dengan implementasi komponen yang berbeda dengan mudah, atau mengonfigurasi objek untuk pengujian secara langsung dengan menyediakan varian tiruan (mis. DAO yang diolok-olok sehingga Anda tidak menekan basis data selama pengujian)
sumber
Orang lain telah menunjuk ke masalah umum (dan merupakan jawaban yang valid), tetapi saya hanya akan menawarkan satu komentar tambahan: bukan bahwa Anda TIDAK PERNAH melakukannya, melainkan melakukannya sesedikit mungkin.
Biasanya ini berarti bahwa hal itu dilakukan tepat sekali: saat bootstrap. Dan kemudian itu hanya untuk mengakses kacang "root", di mana dependensi lain dapat diatasi. Ini bisa menjadi kode yang dapat digunakan kembali, seperti servlet dasar (jika mengembangkan aplikasi web).
sumber
Salah satu tempat Spring adalah menghindari pemasangan . Tentukan dan gunakan Antarmuka, DI, AOP dan hindari menggunakan ApplicationContext.getBean () :-)
sumber
Salah satu alasannya adalah testability. Katakanlah Anda memiliki kelas ini:
Bagaimana Anda bisa menguji kacang ini? Misal seperti ini:
Mudah kan?
Meskipun Anda masih bergantung pada Pegas (karena anotasi), Anda dapat menghapus ketergantungan Anda pada pegas tanpa mengubah kode apa pun (hanya definisi anotasi) dan pengembang pengujian tidak perlu tahu apa pun tentang cara kerja pegas (mungkin ia harus melakukannya, tetapi memungkinkan untuk meninjau dan menguji kode secara terpisah dari apa yang dilakukan pegas).
Masih dimungkinkan untuk melakukan hal yang sama saat menggunakan ApplicationContext. Namun, Anda perlu mengejek
ApplicationContext
yang merupakan antarmuka besar. Anda membutuhkan implementasi dummy atau Anda dapat menggunakan kerangka kerja mengejek seperti Mockito:Ini kemungkinan yang cukup, tetapi saya pikir kebanyakan orang akan setuju bahwa opsi pertama lebih elegan dan membuat tes lebih sederhana.
Satu-satunya opsi yang benar-benar masalah adalah yang ini:
Menguji ini membutuhkan upaya besar atau kacang Anda akan mencoba untuk terhubung ke stackoverflow pada setiap pengujian. Dan segera setelah Anda mengalami kegagalan jaringan (atau admin di stackoverflow memblokir Anda karena tingkat akses yang berlebihan), Anda akan mengalami tes yang gagal secara acak.
Jadi sebagai kesimpulan saya tidak akan mengatakan bahwa menggunakan
ApplicationContext
secara langsung itu salah dan harus dihindari di semua biaya. Namun jika ada opsi yang lebih baik (dan ada dalam banyak kasus), maka gunakan opsi yang lebih baik.sumber
Saya hanya menemukan dua situasi di mana getBean () diperlukan:
Yang lain telah menyebutkan menggunakan getBean () di main () untuk mengambil kacang "utama" untuk program mandiri.
Penggunaan lain yang saya buat dari getBean () adalah dalam situasi di mana konfigurasi pengguna interaktif menentukan susunan kacang untuk situasi tertentu. Jadi, misalnya, bagian dari sistem boot loop melalui tabel database menggunakan getBean () dengan definisi bean scope = 'prototype' dan kemudian menyetel properti tambahan. Agaknya, ada UI yang menyesuaikan tabel database yang akan lebih ramah daripada mencoba (kembali) menulis konteks aplikasi XML.
sumber
Ada waktu lain ketika menggunakan getBean masuk akal. Jika Anda mengkonfigurasi ulang sistem yang sudah ada, di mana dependensi tidak secara eksplisit dipanggil dalam file konteks musim semi. Anda dapat memulai proses dengan melakukan panggilan ke getBean, sehingga Anda tidak harus mengirim semuanya sekaligus. Dengan cara ini Anda perlahan-lahan dapat membangun konfigurasi pegas Anda dengan menempatkan setiap bagian pada tempatnya dari waktu ke waktu dan membuat bit berbaris dengan benar. Panggilan untuk getBean pada akhirnya akan diganti, tetapi ketika Anda memahami struktur kode, atau tidak ada di sana, Anda dapat memulai proses kabel lebih banyak dan lebih banyak kacang dan menggunakan lebih sedikit dan lebih sedikit panggilan untuk getBean.
sumber
Namun, masih ada kasus di mana Anda memerlukan pola pencari lokasi layanan. misalnya, saya memiliki kacang pengontrol, pengontrol ini mungkin memiliki beberapa kacang layanan default, yang dapat disuntikkan ketergantungan oleh konfigurasi. sementara mungkin ada banyak layanan tambahan atau baru controller ini dapat meminta sekarang atau nanti, yang kemudian membutuhkan pencari layanan untuk mengambil kacang layanan.
sumber
Anda harus menggunakan: ConfigurableApplicationContext bukan untuk ApplicationContext
sumber