hello! Semasa pentadbiran JavaRush sedang bekerja pada tahap baharu, saya ingin memulakan satu siri artikel latihan mengenai Rangka Kerja Spring. Ya, saya tahu bahawa sudah terdapat banyak bahan mengenai topik ini di Internet, tetapi, seperti yang ditunjukkan oleh latihan, semuanya berada di peringkat Hello World. Saya ingin bercakap bukan tentang cara meletakkan anotasi dengan betul, tetapi tentang cara semuanya berfungsi "di bawah tudung". Artikel ini ditujukan untuk mereka yang telah bekerja dengan rangka kerja ini dalam satu atau lain cara dan biasa dengan konsep asas.
Memulakan konteks.
Jadi mari kita mulakan dengan asas. Pada pendapat saya, salah satu perkara yang paling penting ialah memahami cara konteks disediakan dan kacang dimulakan. Seperti yang anda ketahui, sebelum Spring mula berfungsi, ia perlu dikonfigurasikan. Pada zaman purba, ini dilakukan menggunakan fail xml (pada beberapa projek, terutamanya yang lama, mereka terus melakukannya hingga ke hari ini). Berikut ialah contoh kecil fail konfigurasi sedemikian:<?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 sudah cukup untuk mencipta beberapa pengawal dan melancarkan permulaan (yang tidak akan berlepas). Tetapi bagaimanakah konfigurasi ini akan menjadikan Spring berfungsi? Dan di sinilah perkara menjadi menarik. Agar konfigurasi kami difahami oleh Spring, terdapat XmlBeanDefinitionReader
. BeanDefinition
Ini ialah komponen Spring dalaman yang mengimbas (menghuraikan) xml dan menciptanya berdasarkan apa yang kami tulis di sana . BeanDefinition
ialah objek yang menyimpan maklumat tentang kacang. Ini termasuk: dari kelas mana ia (kacang) harus dicipta; skop; sama ada permulaan malas dipasang; Adakah perlu untuk memulakan sebelum ini kacang lain dan sifat lain yang diterangkan dalam xml. Semua yang diterima BeanDefinition
ditambahkan pada HashMap
, di mana pengecam ialah nama kacang (ditetapkan oleh anda atau ditetapkan oleh Spring) dan BeanDefinition
objek itu sendiri. Selepas semuanya BeanDefinition
dibuat, seorang wira baru muncul di atas pentas - BeanFactory
. Objek ini berulang pada HashMap’e
s BeanDefinition
, mencipta kacang berdasarkannya dan meletakkannya ke dalam bekas IoC. Terdapat nuansa di sini, sebenarnya, apabila aplikasi bermula, bekas IoC akan mengandungi kacang yang mempunyai skop Singleton (ditetapkan secara lalai), manakala selebihnya dibuat apabila ia diperlukan (prototaip, permintaan, sesi). Dan sekarang penyimpangan kecil, mari kita berkenalan dengan watak lain.
Temui BeanPostProcessor. (BPP)
Hakikatnya ialah kacang tidak semestinya kelas logik perniagaan untuk aplikasi anda. Kacang juga dipanggil kacang infrastruktur. Ringkasnya, kacang infrastruktur ialah kelas yang mengkonfigurasi kacang logik perniagaan anda (ya, terlalu banyak kacang). Saya akan memberitahu anda lebih lanjut mengenainya di bawah, tetapi untuk menjadikannya lebih jelas apa sebenarnya yang dikonfigurasikan BPP, saya akan memberikan contoh. Adakah semua orang biasa dengan ringkasan itu@Autowired
? Jadi, anda AutowiredAnnotationBeanPostProcessor
bertanggungjawab untuk memastikan semua kelas anda dibenamkan antara satu sama lain.
Mari kembali ke BeanFactory
Mengetahui sekarang tentang BPP, anda perlu menjelaskan bahawa apabila mengulangiHashMap
's, BeanDefinition
semua 's pertama kali dibuat dan diletakkan secara berasingan (bukan dalam bekas IoC) BeanPostProcessor
. Selepas ini, kacang biasa logik perniagaan kami dicipta, dimasukkan ke dalam bekas IoC, dan konfigurasinya mula menggunakan BPP tertunda secara berasingan. Dan ini adalah bagaimana ia berlaku, setiap BPP mempunyai 2 kaedah:
postProcessorBeforeInitialization(Object bean, String beanName);
postProcessorAfterInitialization(Object bean, String beanName);
Berulang melalui tong sampah kami dua kali. Kali pertama kaedah dipanggil postProcessorBeforeInitialization
, dan kali kedua kaedah dipanggil postProcessorAfterInitialization
. Pastinya telah timbul persoalan mengapa dua kaedah diperlukan, biar saya jelaskan. Hakikatnya ialah untuk memproses beberapa anotasi (seperti @Transactional
, contohnya), kacang kami digantikan dengan kelas proksi. Untuk memahami sebab ini dilakukan, anda perlu tahu cara ia berfungsi @Transactional
, dan ini adalah cara ia berfungsi. Anda perlu menambah beberapa baris lagi kod pada kaedah yang ditandakan dengan anotasi ini dengan cepat. Bagaimana hendak melakukannya? Betul, dengan mencipta kelas proksi, di dalamnya kod yang diperlukan akan ditambahkan pada kaedah yang diperlukan. Sekarang bayangkan keadaan ini, kami mempunyai kelas:
class A {
@Autowired
private SomeClass someClass;
@Transactional
public void method() {
// модификатор доступа обязательно public
}
}
Kelas ini mempunyai 2 anotasi @Autowired
dan @Transactional
. Kedua-dua anotasi diproses oleh BPP yang berbeza. Jika ia berfungsi dahulu AutowiredBPP
, maka semuanya akan baik-baik saja, tetapi jika tidak, maka kita akan menghadapi masalah ini. Hakikatnya ialah apabila kelas proksi dibuat, semua maklumat meta hilang. Dalam erti kata lain, tidak akan ada maklumat tentang anotasi @Autowired
dalam kelas proksi, dan oleh itu AutowiredBPP
ia tidak akan berfungsi, yang bermaksud medan kami someClass
akan mempunyai nilai null
, yang kemungkinan besar akan membawa kepada NPE. Perlu juga diketahui bahawa antara panggilan kaedah, kaedah postProcessorBeforeInitialization
-dipanggil , jika ada. Ini pada asasnya adalah pembina kedua, tetapi perbezaannya ialah pada masa ini semua kebergantungan kami sudah tertanam dalam kelas dan kami boleh mengaksesnya dari kaedah -. Jadi, sekali lagi algoritma permulaan konteks: postProcessorAfterInitialization
init
init
XmlBeanDefinitionReader
mengimbas fail konfigurasi xml kami.- Mencipta
BeanDefinition
dan memasukkannya ke dalamHashMap
. - Datang
BeanFactory
dan daripada iniHashMap
secara berasingan menambah semuaBeanPostProcessor
's. - Mencipta
BeanDefinition
kacang daripada 's dan meletakkannya dalam bekas IoC. - Di sini BPP datang dan mengkonfigurasi kacang ini menggunakan 2 kaedah.
- sedia.
GO TO FULL VERSION