Peluang apa yang diberikan Spring untuk menyesuaikan perilakunya?

Halo semuanya. Dalam sentuhan Vladislav Rodin. Saat ini, saya adalah kepala kursus Arsitek Beban Tinggi di OTUS, dan saya juga mengajar kursus arsitektur perangkat lunak.

Selain mengajar, saya juga menulis materi hak cipta untuk blog OTUS di Haber dan saya ingin bertepatan dengan artikel hari ini untuk meluncurkan kursus "Developer on the Spring Framework" , yang sekarang terbuka untuk perekrutan.




pengantar


Dari sudut pandang pembaca, kode aplikasi menggunakan Spring terlihat cukup sederhana: beberapa kacang dideklarasikan, kelas ditandai dengan anotasi, dan kemudian kacang disuntikkan jika perlu, semuanya berfungsi dengan baik. Tetapi seorang pembaca yang ingin tahu memiliki pertanyaan: "Bagaimana cara kerjanya? Apa yang terjadi?". Pada artikel ini kami akan mencoba menjawab pertanyaan ini, tetapi tidak demi memuaskan keingintahuan kosong.

Kerangka kerja pegas dikenal cukup fleksibel dan menyediakan opsi untuk menyesuaikan perilaku kerangka kerja. Spring juga memiliki sejumlah aturan yang agak menarik untuk menerapkan anotasi tertentu (misalnya, Transaksional). Untuk memahami arti dari aturan ini, untuk dapat menurunkannya, dan juga untuk memahami apa dan bagaimana Anda dapat mengkonfigurasi di Spring, Anda perlu memahami beberapa prinsip operasi apa yang ada di Spring di bawah tenda. Seperti yang Anda ketahui, pengetahuan tentang beberapa prinsip dibebaskan dari pengetahuan banyak fakta. Saya sarankan agar Anda membiasakan diri dengan prinsip-prinsip di bawah ini jika, tentu saja, Anda belum mengetahuinya.

Konfigurasi membaca


Pada awalnya, Anda perlu mem-parsing konfigurasi yang ada di aplikasi Anda. Karena ada beberapa jenis konfigurasi (xml-, groovy-, konfigurasi java, konfigurasi berdasarkan anotasi), metode yang berbeda digunakan untuk membacanya. Dengan satu atau lain cara, peta dari bentuk Peta <String, BeanDefinition> dikumpulkan, di mana nama-nama kacang ditugaskan untuk definisi kacang mereka. Objek dari kelas BeanDefinition adalah meta-informasi tentang kacang dan berisi id-id kacang itu, namanya, kelasnya, metode destroy dan init.

Contoh kelas yang terlibat dalam proses ini: GroovyBeanDefinitionReader, XmlBeanDefinitionReader, AnnotatedBeanDefinitionReader, mengimplementasikan antarmuka BeanDefinitionReader .

Menyiapkan Beandefinitions


Jadi, kami memiliki deskripsi kacang, tetapi tidak ada kacang itu sendiri, belum dibuat. Sebelum membuat kacang, Spring menyediakan kemampuan untuk menyesuaikan BeanDefinitions yang dihasilkan. Untuk tujuan ini, antarmuka BeanFactoryPostProcessor digunakan . Ini terlihat seperti ini:

public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

Parameter metode antarmuka ini memungkinkan menggunakan metode getBeanDefinitionNames sendiri untuk mendapatkan nama yang digunakan untuk mendapatkan BeanDefinitions dari peta dan mengeditnya.

Mengapa ini diperlukan? Misalkan beberapa kacang memerlukan detail untuk terhubung ke sistem eksternal, seperti database. Kami ingin kacang yang sudah dibuat dengan detail, tetapi rincian itu sendiri disimpan dalam file properti. Kita dapat menerapkan salah satu standar BeanFactoryPostProcessors - PropertySourcesPlaceholderConfigurer, yang akan menggantikan properti nama di BeanDefinition dengan nilai aktual yang disimpan dalam file properti. Artinya, itu benar-benar akan menggantikan Value ("user") dengan Value ("root") di BeanDefinion. Agar ini berfungsi, PropertySourcesPlaceholderConfigurer, tentu saja, harus terhubung. Tetapi ini tidak terbatas pada ini, Anda dapat mendaftarkan BeanFactoryPostProcessor Anda, untuk menerapkan logika apa pun yang Anda perlukan untuk memproses BeanDefinitions.

Membuat Kacang


Pada tahap ini, kami memiliki peta yang memiliki nama-nama kacang yang terletak oleh kunci, dan konfigurasi BeanDefinitions berdasarkan nilai-nilai. Sekarang Anda perlu membuat kacang ini. Inilah yang dilakukan BeanFactory . Tetapi di sini Anda dapat menambahkan penyesuaian dengan menulis dan mendaftarkan FactoryBean Anda . FactoryBean adalah antarmuka bentuk:

public interface FactoryBean {
    T getObject() throws Exception;
    Class<?> getObjectType();
    boolean isSingleton();
}

Jadi, BeanFactory membuat kacang itu sendiri jika tidak ada FactoryBean yang sesuai dengan kelas kacang, atau meminta FactoryBean untuk membuat kacang ini. Ada nuansa kecil: jika ruang lingkup kacang adalah singletone, maka kacang dibuat pada tahap ini, jika prototipe, maka setiap kali kacang ini diperlukan, itu akan diminta dari BeanFactory.

Hasilnya, kami kembali mendapatkan peta, tetapi sudah sedikit berbeda: kunci berisi nama kacang, dan nilai kacang itu sendiri. Tetapi ini hanya berlaku untuk lajang.

Mengkonfigurasi Kacang


Sekarang tibalah tahap yang paling menarik. Kami memiliki peta berisi kacang yang dibuat, tetapi kacang ini belum dikonfigurasi. Artinya, kami tidak memproses anotasi yang menetapkan status kacang: Autowired, Value. Kami juga tidak memproses anotasi yang mengubah perilaku kacang: Transaksional, Async. BeanPostProcessors , yang sekali lagi implementasi dari antarmuka yang sesuai, memungkinkan kami untuk menyelesaikan masalah ini :

public interface BeanPostProcessor {
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

Kami melihat 2 metode dengan nama yang menakutkan tetapi komprehensif. Kedua metode mengambil kacang sebagai input, dari mana Anda dapat bertanya kelas mana itu, dan kemudian menggunakan API Refleksi untuk memproses anotasi. Metode pengembalian kacang, mungkin diganti dengan proxy.

Untuk setiap kacang sebelum menempatkannya dalam konteks, hal berikut terjadi: metode postProcessBeforeInisialisasi dipicu untuk semua BeanPostProcessors, maka metode init dipicu, dan kemudian metode postProcessAfterInisialisasi dipicu untuk semua BeanPostProcessors juga.

Dua metode ini memiliki semantik yang berbeda: postProcessBeforeInisialisasi proses menyatakan penjelasan, postProcessAfterInisialisasi memproses perilaku, karena proksi digunakan untuk memproses perilaku, yang dapat menyebabkan hilangnya anotasi. Itu sebabnya perilaku berubah di tempat terakhir.

Di mana kustomisasi? Kita dapat menulis anotasi kami, BeanPostProcessor untuk itu, dan Spring akan memprosesnya. Namun, agar BeanPostProcessor berfungsi, ia juga harus terdaftar sebagai kacang.

Misalnya, untuk menyematkan nomor acak dalam bidang, kami membuat anotasi InjectRandomInt (tergantung di bidang), membuat dan mendaftarkan InjectRandomIntBeanPostProcessor, dengan metode pertama yang kami proses anotasi yang dibuat, dan dalam metode kedua, kami cukup mengembalikan kacang yang masuk.

Untuk profil kacang, membuat anotasi Profil yang disiarkan ke metode, membuat dan mendaftarkan ProfileBeanPostProcessor, dengan metode pertama di mana kita mengembalikan kacang yang masuk, dan dalam metode kedua, kita mengembalikan proxy yang membungkus panggilan ke metode asli dengan kliping dan waktu eksekusi logging.



Pelajari lebih lanjut tentang kursus



All Articles