Halo! Sementara administrasi JavaRush sedang mengerjakan level baru, saya ingin memulai serangkaian artikel pelatihan tentang Spring Framework. Ya, saya tahu bahwa sudah ada banyak materi tentang topik ini di Internet, tetapi, seperti yang ditunjukkan oleh latihan, semuanya berada di tingkat Hello World. Saya ingin berbicara bukan tentang cara menempatkan anotasi dengan benar, tetapi tentang cara kerjanya "di balik terpal". Artikel ini ditujukan bagi mereka yang telah bekerja dengan kerangka ini dalam satu atau lain cara dan akrab dengan konsep dasar.
Menginisialisasi konteks.
Jadi mari kita mulai dengan dasar-dasarnya. Menurut pendapat saya, salah satu poin terpenting adalah memahami bagaimana konteks diatur dan kacang diinisialisasi. Seperti yang Anda ketahui, sebelum Spring mulai bekerja, Spring perlu dikonfigurasi. Pada zaman dahulu, hal ini dilakukan dengan menggunakan file xml (pada beberapa proyek, terutama proyek lama, hal ini terus dilakukan hingga hari ini). Berikut adalah contoh kecil dari file konfigurasi tersebut:<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="helloWorld" class="ru.javarush.HelloWorld">
<property name="message" value="Hello World!"/>
</bean>
</beans>
Pada umumnya, ini cukup untuk membuat beberapa pengontrol dan meluncurkan startup (yang tidak akan lepas landas). Namun bagaimana konfigurasi ini membuat Spring berfungsi? Dan di sinilah hal-hal menjadi menarik. Agar konfigurasi kita dapat dipahami oleh Spring, ada file XmlBeanDefinitionReader
. BeanDefinition
Ini adalah komponen Spring internal yang memindai (mem-parsing) xml dan membuat berdasarkan apa yang kami tulis di sana . BeanDefinition
adalah objek yang menyimpan informasi tentang kacang. Hal ini mencakup: dari kelas mana kacang tersebut harus dibuat; cakupan; apakah inisialisasi lambat diinstal; Apakah perlu menginisialisasi sebelum kacang ini properti lain dan lainnya yang dijelaskan dalam xml. Semua yang diterima BeanDefinition
ditambahkan ke HashMap
, yang pengidentifikasinya adalah nama kacang (ditetapkan oleh Anda atau ditetapkan oleh Spring) dan BeanDefinition
objek itu sendiri. Setelah semuanya BeanDefinition
dibuat, pahlawan baru muncul di panggung - BeanFactory
. Objek ini mengulangi HashMap’e
s BeanDefinition
, membuat kacang berdasarkan pada objek tersebut dan memasukkannya ke dalam wadah IoC. Ada nuansa di sini, sebenarnya ketika aplikasi dimulai, container IoC akan berisi beans yang memiliki cakupan Singleton (diatur secara default), sedangkan sisanya dibuat saat dibutuhkan (prototipe, permintaan, sesi). Dan sekarang penyimpangan kecil, mari berkenalan dengan karakter lain.
Temui BeanPostProcessor. (BPP)
Faktanya adalah kacang belum tentu merupakan kelas logika bisnis untuk aplikasi Anda. Kacang juga disebut kacang infrastruktur. Singkatnya, kacang infrastruktur adalah kelas yang mengonfigurasi kacang logika bisnis Anda (ya, terlalu banyak kacang). Saya akan memberi tahu Anda lebih banyak tentangnya di bawah ini, tetapi untuk membuatnya lebih jelas apa sebenarnya yang dikonfigurasi BPP, saya akan memberikan sebuah contoh. Apakah semua orang familiar dengan ringkasannya@Autowired
? Jadi, Anda AutowiredAnnotationBeanPostProcessor
bertanggung jawab untuk memastikan bahwa semua kelas Anda tertanam satu sama lain.
Mari kita kembali ke BeanFactory
Mengetahui sekarang tentang BPP, Anda perlu mengklarifikasi bahwa ketika melakukan iterasiHashMap
, BeanDefinition
semua terlebih dahulu dibuat dan ditempatkan secara terpisah (bukan dalam wadah IoC) BeanPostProcessor
. Setelah ini, kacang reguler logika bisnis kami dibuat, dimasukkan ke dalam wadah IoC, dan konfigurasinya mulai menggunakan BPP yang ditangguhkan secara terpisah. Dan beginilah kejadiannya, setiap BPP mempunyai 2 cara :
postProcessorBeforeInitialization(Object bean, String beanName);
postProcessorAfterInitialization(Object bean, String beanName);
Iterasi melalui bin kami dua kali. Pertama kali metode dipanggil postProcessorBeforeInitialization
, dan kedua kali metode dipanggil postProcessorAfterInitialization
. Tentunya muncul pertanyaan mengapa diperlukan dua metode, izinkan saya menjelaskannya. Faktanya adalah untuk memproses beberapa anotasi (seperti @Transactional
, misalnya), kacang kita digantikan oleh kelas proxy. Untuk memahami mengapa hal ini dilakukan, Anda perlu mengetahui cara kerjanya @Transactional
, dan inilah cara kerjanya. Anda perlu menambahkan beberapa baris kode lagi ke metode yang ditandai dengan anotasi ini dengan cepat. Bagaimana cara melakukannya? Benar, dengan membuat kelas proxy, di dalamnya kode yang diperlukan akan ditambahkan ke metode yang diperlukan. Sekarang bayangkan situasi ini, kita mempunyai kelas:
class A {
@Autowired
private SomeClass someClass;
@Transactional
public void method() {
// модификатор доступа обязательно public
}
}
Kelas ini memiliki 2 anotasi @Autowired
dan @Transactional
. Kedua anotasi tersebut diproses oleh BPP yang berbeda. Jika berhasil terlebih dahulu AutowiredBPP
, maka semuanya akan baik-baik saja, tetapi jika tidak, maka kita akan menemui masalah ini. Faktanya adalah ketika kelas proxy dibuat, semua informasi meta hilang. Dengan kata lain, tidak akan ada informasi tentang anotasi @Autowired
di kelas proxy, dan oleh karena AutowiredBPP
itu tidak akan berfungsi, yang berarti bidang kita someClass
akan memiliki nilai null
, yang kemungkinan besar akan menghasilkan NPE. Perlu juga diketahui bahwa di antara pemanggilan metode, -metode dipanggil postProcessorBeforeInitialization
, jika ada. Ini pada dasarnya adalah konstruktor kedua, tetapi perbedaannya adalah saat ini semua dependensi kita sudah tertanam di kelas dan kita dapat mengaksesnya dari -metode. Jadi, sekali lagi algoritma inisialisasi konteks: postProcessorAfterInitialization
init
init
XmlBeanDefinitionReader
memindai file konfigurasi xml kami.- Membuat
BeanDefinition
dan menempatkannya di dalamnyaHashMap
. - Datang
BeanFactory
dan dari iniHashMap
secara terpisah menambahkan semuaBeanPostProcessor
's. - Membuat
BeanDefinition
kacang dari 's dan menempatkannya dalam wadah IoC. - Di sini BPP datang dan mengkonfigurasi kacang ini menggunakan 2 metode.
- Siap.
GO TO FULL VERSION