Di kerajaan tertentu, tidak dalam kondisi "melonjak". Laporan Yandex

Spring adalah framework Java open source yang kuat. Saya memutuskan untuk memberi tahu Anda tugas apa yang digunakan oleh backend Spring dan untuk apa pro dan kontra dibandingkan dengan perpustakaan lain: Guice and Dagger 2. Pertimbangkan injeksi dependensi dan inversi kontrol - Anda akan belajar cara mulai mempelajari prinsip-prinsip ini.


- Halo semuanya, nama saya Cyril. Hari ini saya akan berbicara tentang Injeksi Ketergantungan.

Kami akan mulai dengan apa yang disebut laporan saya. "Di kerajaan tertentu, bukan di" musim semi "." Kami akan berbicara, tentu saja, tentang Spring, tetapi saya juga ingin melihat semua yang ada di sampingnya. Khususnya apa yang akan kita bicarakan?



Saya akan melakukan penyimpangan kecil - memberi tahu Anda apa yang saya kerjakan, apa proyek saya, mengapa kami menggunakan Dependency Injection. Maka saya akan memberi tahu Anda tentang semua ini, membandingkan Pembalikan Kontrol dan Ketergantungan Injeksi, dan berbicara tentang penerapannya di tiga perpustakaan paling terkenal.

Saya bekerja di tim Yandex.Tracker. Kami membuat analog kelontong Jira atau Trello. [...] Kami memutuskan untuk membuat produk kami sendiri, yang merupakan internal pertama. Sekarang kami menjualnya. Anda masing-masing dapat masuk, membuat Pelacak Anda sendiri dan melakukan tugas - misalnya, pendidikan atau bisnis.

Mari kita lihat antarmuka. Dalam contoh, saya akan menggunakan beberapa istilah dari daerah saya. Kami akan mencoba membuat tiket dan melihat komentar yang akan ditinggalkan oleh rekan kerja lain di dalamnya.

Untuk mulai dengan, apa Dependency Injection secara umum? Ini adalah pola pemrograman yang memenuhi pepatah Amerika kuno, prinsip Hollywood: "Jangan panggil kami, kami akan memanggil Anda sendiri." Ketergantungan datang kepada kita. Ini terutama sebuah pola, bukan perpustakaan. Oleh karena itu, pada prinsipnya, pola seperti itu biasa terjadi di hampir semua tempat. Anda bahkan bisa mengatakan bahwa semua aplikasi menggunakan Injeksi Ketergantungan dalam satu atau lain cara.



Mari kita lihat sendiri bagaimana Anda bisa menggunakan Dependency Injection jika kita mulai dari awal. Misalkan saya memutuskan untuk mengembangkan kelas sekecil itu di mana saya akan membuat tiket melalui API kami. Misalnya, buat turunan dari kelas TrackerApi. Ini memiliki metode createTicket di mana kami akan mengirim email saya. Kami akan membuat tiket dari bawah akun saya dengan nama: "Siapkan laporan untuk Java Meetup".



Mari kita lihat implementasi TrackerApi. Di sini, misalnya, kita dapat melakukan ini: membuat contoh httpClient. Dalam istilah sederhana, kita akan membuat objek yang akan kita gunakan menuju API. Melalui objek ini kita akan memanggil metode eksekusi di atasnya.



Misalnya, yang khusus. Saya menulis kode eksternal dari kelas-kelas ini, dan akan menggunakannya seperti ini. Saya membuat TicketCreator baru dan memanggil metode createTicket di atasnya.



Ada masalah di sini - setiap kali kami membuat tiket, kami akan membuat ulang dan membuat kembali httpClient, meskipun secara umum, tidak perlu untuk ini. httpClients sangat serius untuk dibuat.



Mari kita coba membuatnya. Di sini Anda dapat melihat contoh pertama Dependency Injection dalam kode kami. Perhatikan apa yang telah kita lakukan. Kami mengambil variabel kami di lapangan dan mengisinya di konstruktor. Fakta bahwa kita mengisinya dalam konstruktor berarti bahwa ketergantungan datang kepada kita. Ini adalah Injeksi Ketergantungan pertama.



Kami mengalihkan tanggung jawab kepada pengguna kode, jadi sekarang kami harus membuat httpClient, meneruskannya, misalnya, ke TicketCreator.



Ini juga tidak terlalu bagus di sini, karena sekarang, dengan memanggil metode ini, kita akan kembali membuat httpClient setiap waktu.



Karena itu, kami bawa lagi ke lapangan. Dan di sini, omong-omong, ada contoh Ketergantungan Injeksi yang tidak jelas. Kami dapat mengatakan bahwa kami selalu membuat tiket dari bawah saya (atau dari bawah orang lain). Kami akan membuat setiap objek TicketCreator terpisah dari pengguna yang berbeda.

Misalnya, ini akan dibuat dari bawah saya ketika kita membuatnya. Dan garis yang kita lewati ke konstruktor juga merupakan Dependency Injection.



Bagaimana yang akan kita lakukan sekarang? Buat instance baru TrackerTicketCreator dan panggil metode tersebut. Sekarang kita bahkan dapat membuat semacam metode khusus yang akan membuat tiket dengan teks khusus untuk kita. Misalnya, buat tiket "Sewa peserta pelatihan baru".



Sekarang mari kita coba untuk melihat seperti apa kode kita jika kita ingin membaca komentar di tiket ini dengan cara yang sama, dari bawah saya. Ini tentang kode yang sama. Kami akan memanggil metode getComments pada tiket ini.



Akan seperti apa dia? Jika kami mengambil dan menduplikasi fungsi ini dalam pembaca komentar, kami menduplikasi pembuatan httpClient. Ini tidak cocok untuk kita. Kami ingin menyingkirkannya.



Bagus Sekarang mari kita meneruskan semua parameter ini sebagai Injeksi Ketergantungan, sebagai parameter konstruktor.



Apa masalahnya di sini? Kami melewatkan semuanya, tetapi dalam kode pengguna kami sekarang menulis "boilerplate". Ini adalah semacam kode yang tidak perlu yang biasanya perlu ditulis pengguna untuk melakukan tindakan yang relatif kecil dalam hal logika. Di sini kita harus terus-menerus membuat httpClient, API untuknya dan memilih email pengguna. Setiap pengguna TicketCreator harus melakukan ini sendiri. Ini tidak baik. Kami sekarang akan mencoba untuk melihat tampilannya di perpustakaan ketika kami mencoba menghindarinya.

Sekarang, mari kita sedikit menyimpang dan melihat apa itu Inversion of Control, karena banyak yang mengaitkan Injeksi Ketergantungan dengannya.



Inversion of Control adalah prinsip pemrograman di mana objek yang kita gunakan tidak dibuat oleh kami. Kami tidak mempengaruhi siklus hidup mereka sama sekali. Biasanya, entitas yang membuat objek-objek ini disebut wadah IoC. Banyak dari Anda telah mendengar tentang Spring di sini. Dokumentasi Spring mengatakan bahwa IoC juga dikenal sebagai Injeksi Ketergantungan. Mereka percaya bahwa ini adalah satu dan sama.



Apa prinsip dasarnya? Objek dibuat bukan oleh kode aplikasi, tetapi oleh beberapa wadah IoC. Kami, sebagai pengguna perpustakaan, tidak melakukan apa-apa, semuanya datang kepada kami sendiri. Tentu saja, IoC adalah relatif. Kontainer IoC sendiri menciptakan objek-objek ini, dan ini tidak lagi berlaku untuknya. Anda mungkin berpikir bahwa IoC tidak hanya mengimplementasikan perpustakaan DI. Perpustakaan Java yang terkenal Servlets dan Akka Actors, yang sekarang digunakan dalam Scala dan dalam kode Java.



Mari kita bicara tentang perpustakaan. Secara umum, cukup banyak perpustakaan yang telah ditulis untuk Jawa dan Kotlin. Saya akan daftar yang utama:

- Spring, kerangka kerja yang bagus. Bagian utamanya adalah Injeksi Ketergantungan atau, seperti kata mereka, Inversion of Control.
- Guice adalah perpustakaan yang ditulis kira-kira antara Spring kedua dan ketiga ketika Spring dipindahkan dari XML ke deskripsi kode. Begitulah, ketika Musim Semi masih belum begitu indah.
- Belati adalah apa yang biasanya digunakan orang di Android.

Mari kita coba menulis ulang contoh kita di Spring.



Kami memiliki TrackerApi kami. Saya tidak memasukkan pengguna ke sini secara singkat. Misalkan kita coba di Dependency Injection untuk melakukan httpClient. Untuk melakukan ini, kita perlu mendeklarasikannya dengan anotasi. Komponen , seluruh kelas, dan khususnya konstruktor, dideklarasikan dengan anotasi Autowired . Apa artinya ini untuk Spring?



Kami memiliki konfigurasi seperti itu dalam kode, ini ditunjukkan oleh anotasi Pemindaian Komponen . Ini berarti bahwa kita akan mencoba untuk menelusuri seluruh pohon kelas kita dalam paket yang berisinya. Dan lebih jauh ke pedalaman kita akan mencoba untuk menemukan semua kelas yang ditandai dalam anotasi Komponen .



Komponen-komponen ini akan jatuh ke wadah IoC. Penting bagi kita bahwa semuanya jatuh cinta pada kita. Kami hanya menandai apa yang ingin kami umumkan. Agar sesuatu datang kepada kami, kami harus mendeklarasikannya menggunakan anotasi Autowired di konstruktor.



TicketCreator kami lakukan dengan cara yang persis sama.



Dan CommentReader juga.



Sekarang mari kita lihat kembali konfigurasi. Seperti yang kami katakan, Pemindaian Komponen akan meletakkan semuanya dalam wadah IoC. Tetapi ada satu titik, yang disebut metode pabrik. Kami memiliki metode httpClient, yang tidak kami buat sebagai kelas, karena httpClient datang kepada kami dari pustaka. Dia tidak mengerti apa itu Spring, dan sebagainya. Kami akan membuatnya langsung di konfigurasi. Untuk melakukan ini, kita menulis metode yang biasanya membangunnya sekali, dan menandainya dengan anotasi Bean.



Apa pro dan kontra? Plus utama - Musim semi sangat umum di dunia. Plus dan minus berikutnya adalah autoscanning. Kita tidak boleh secara eksplisit menyatakan di mana saja bahwa kita ingin menambahkan wadah ke IoC selain penjelasan atas kelas itu sendiri. Penjelasan yang cukup. Dan minusnya persis sama: jika, sebaliknya, kami ingin kontrol atas ini, maka Spring tidak memberi kami ini. Kecuali kita dapat mengatakan di tim kami: “Tidak, kami tidak akan melakukan itu. Kita harus dengan jelas meresepkan sesuatu di suatu tempat. Hanya dalam konfigurasi, seperti yang kita lakukan dengan kacang.

Juga, karena ini, awal yang lambat terjadi. Ketika aplikasi dimulai, Spring harus melalui semua kelas ini dan mencari tahu apa yang harus dimasukkan ke dalam wadah IoC. Ini memperlambatnya. Kelemahan terbesar Spring, menurut saya, adalah pohon ketergantungan. Itu tidak diperiksa pada tahap kompilasi. Ketika Spring mulai pada suatu titik, perlu dipahami jika saya memiliki ketergantungan di dalamnya. Jika nanti ternyata tidak ada di pohon dependensi, maka Anda akan mendapatkan kesalahan dalam runtime. Dan kami di Jawa tidak ingin kesalahan runtime. Kami ingin kode tersebut dikompilasi untuk kami. Ini berarti berhasil.



Mari kita lihat Guice. Ini adalah perpustakaan yang, seperti yang saya katakan, dibuat antara musim semi kedua dan ketiga. Keindahan yang kami lihat bukan. Ada XML. Untuk memperbaiki masalah ini, dan ditulis oleh Guice. Dan di sini Anda dapat melihat bahwa, tidak seperti konfigurasi, kami menulis modul. Di dalamnya, kami secara eksplisit mendeklarasikan kelas mana yang ingin kami masukkan dalam modul ini: TrackerAPI, TrackerTicketCreator dan semua nampan lainnya. Sebuah analog dengan anotasi Bean di sini adalah Provides , yang membuat httpClient dengan cara yang sama.



Kita perlu mendeklarasikan masing-masing kacang ini. Kami akan memberi contoh Singleton . Tetapi secara khusus, Singleton akan mengatakan bahwa kacang seperti itu akan dibuat tepat satu kali. Kami tidak akan terus-menerus membuatnya kembali. Dan Suntikan , masing-masing, adalah analog dari Autowired .



Tablet kecil dengan apa yang menjadi miliknya.



Apa pro dan kontra? Pro: itu lebih sederhana, menurut saya, dan dapat dimengerti daripada versi XML Spring. Startup lebih cepat. Dan inilah kontra: membutuhkan deklarasi eksplisit dari kacang yang digunakan. Kita seharusnya menulis Bean. Tetapi di sisi lain, ini merupakan nilai tambah, seperti yang telah kita katakan. Ini adalah bayangan cermin dari apa yang dimiliki Spring. Tentu saja, itu kurang umum daripada Spring. Ini adalah minus alaminya. Dan ada masalah yang sama persis - pohon ketergantungan tidak diperiksa pada tahap kompilasi.

Ketika mereka mulai menggunakan Guice untuk Android, mereka menyadari bahwa mereka masih kekurangan kecepatan peluncuran. Oleh karena itu, mereka memutuskan untuk menulis kerangka kerja Ketergantungan Injeksi yang lebih sederhana dan lebih primitif yang akan memungkinkan mereka untuk memulai aplikasi dengan cepat, karena untuk Android sangat penting.



Di sini terminologinya sama. Belati memiliki modul yang persis sama dengan Guice. Tetapi mereka sudah ditandai dengan anotasi, tidak seperti dalam kasus warisan dari kelas. Karena itu, prinsipnya dipertahankan.

Satu-satunya minus adalah bahwa kita harus selalu secara eksplisit menunjukkan dalam modul bagaimana kacang dibuat. Di Guice, kita bisa memberikan kreasi kacang di dalam kacang itu sendiri. Kami tidak harus mengatakan dependensi seperti apa yang perlu kami teruskan. Dan di sini kita perlu secara eksplisit mengatakan ini.



Di Dagger, karena Anda tidak ingin melakukan input manual terlalu, ada konsep komponen. Komponen adalah sesuatu yang mengikat modul ketika kita ingin mendeklarasikan satu bin dari satu modul sehingga dapat diambil di modul lain. Ini adalah konsep yang berbeda. Kacang dari satu modul dapat "menyuntikkan" kacang dari modul lain menggunakan komponen.



Berikut ini tentang pelat ringkasan yang sama - apa yang telah berubah atau belum berubah dalam kasus Suntikan atau modul.



Apa kelebihannya? Itu bahkan lebih sederhana dari Guice. Peluncuran bahkan lebih cepat dari Guice. Dan itu mungkin tidak akan menjadi lebih cepat lagi, karena belati benar-benar meninggalkan pantulan. Ini persis bagian dari perpustakaan di Jawa yang bertanggung jawab untuk melihat keadaan objek, kelas dan metodenya. Artinya, dapatkan status dalam runtime. Karena itu, tidak menggunakan refleksi. Dia tidak pergi dan tidak memindai dependensi apa yang dimiliki seseorang. Tetapi karena ini, ia memulai dengan sangat cepat.

Bagaimana dia melakukannya? Menggunakan pembuatan kode.



Jika kita melihat ke belakang, kita akan melihat komponen antarmuka. Kami tidak mengimplementasikan implementasi antarmuka ini, Dagger melakukannya untuk kami. Dan dimungkinkan untuk lebih jauh menggunakan antarmuka dalam aplikasi.



Secara alami, ini sangat umum di dunia Android karena kecepatan ini. Pohon dependensi diperiksa segera pada kompilasi, karena tidak ada yang akan kami tunda saat runtime.

Apa kerugiannya? Dia memiliki lebih sedikit peluang. Ini lebih bertele-tele daripada Guice dan Spring.



Di dalam perpustakaan-perpustakaan ini, sebuah inisiatif muncul di Jawa - yang disebut JSR-330. JSR adalah permintaan untuk membuat perubahan dalam spesifikasi bahasa atau menambahnya dengan beberapa perpustakaan tambahan. Standar semacam itu diusulkan berdasarkan Guice, dan anotasi Suntikan ditambahkan ke perpustakaan ini. Dengan demikian, Spring and Guice mendukungnya.

Kesimpulan apa yang bisa ditarik? Java memiliki banyak pustaka yang berbeda untuk DI. Dan Anda perlu memahami mengapa kami mengambil salah satu dari mereka. Jika kami menggunakan Android, maka sudah tidak ada pilihan, kami menggunakan Belati. Jika kita pergi ke dunia backend, maka kita sudah melihat apa yang paling cocok untuk kita. Dan untuk studi pertama Ketergantungan Injeksi, menurut saya bahwa Guice lebih baik daripada Musim Semi. Tidak ada yang berlebihan di dalamnya. Anda dapat melihat cara kerjanya, rasakan.

Untuk studi lebih lanjut, saya sarankan agar Anda membiasakan diri dengan dokumentasi semua perpustakaan ini dan komposisi JSR:
- Musim semi
- Guice
- Belati 2
- JSR-330

Terima kasih

Source: https://habr.com/ru/post/id478788/


All Articles