Prototipe IOC pegas
Inti dasar dan titik awal kerangka kerja musim semi tidak diragukan lagi adalah IOC. Sebagai teknologi inti yang disediakan oleh Containers Spring, IOC berhasil menyelesaikan inversi dependensi: dari manajemen aktif kelas utama pada dependensi hingga kontrol global dependensi oleh kontainer musim semi.
Apa manfaat dari melakukan ini?
Tentu saja, itu adalah apa yang disebut "decoupling", yang dapat membuat hubungan antara modul-modul program lebih mandiri. Spring hanya perlu mengontrol ketergantungan antara modul -modul ini dan membuat, mengelola, dan memelihara modul -modul ini berdasarkan dependensi ini selama startup kontainer dan proses inisialisasi. Jika Anda perlu mengubah dependensi antar modul, Anda bahkan tidak perlu mengubah kode program. Anda hanya perlu memodifikasi dependensi yang diubah. Musim semi akan membangun kembali ketergantungan baru ini dalam proses memulai dan menginisialisasi wadah lagi. Dalam proses ini, perlu dicatat bahwa kode itu sendiri tidak perlu mencerminkan deklarasi situasi ketergantungan spesifik modul tetapi hanya perlu menentukan antarmuka modul yang diperlukan. Oleh karena itu, ini adalah ide yang berorientasi antarmuka yang khas. Pada saat yang sama, yang terbaik adalah mengekspresikan dependensi dalam bentuk file konfigurasi atau anotasi. Kelas pemrosesan pegas yang relevan akan merakit modul berdasarkan file konfigurasi eksternal ini, atau memindai anotasi untuk memanggil prosesor anotasi internal untuk merakit modul untuk menyelesaikan proses IOC.
Tujuan IOC adalah injeksi ketergantungan yang disebut DI. Melalui teknologi IOC, wadah pamungkas akan membantu kami melengkapi injeksi ketergantungan di antara modul.
Selain itu, poin terakhir adalah bahwa dalam proses IOC musim semi, kita harus selalu jelas tentang jalur utama di atas. Tidak peduli seberapa rumit sintaksis real-time dan struktur kelas, fungsi dan tujuannya sama: itu adalah untuk menyelesaikan "perakitan" modul dengan mengandalkan file konfigurasi yang dijelaskan sebagai perakitan "gambar". Sintaks yang kompleks hanyalah sarana untuk mencapai tujuan ini.
Prototipe IOC yang disebut, untuk menunjukkan diagram skematik IOC paling sederhana, kita mungkin juga membuat prototipe yang benar-benar sederhana untuk menggambarkan proses ini:
Pertama, ada beberapa modul yang kami tentukan, termasuk modul utama dan modul ketergantungan yang ditentukan oleh dua antarmuka:
kelas MainModule {private DependModulea modulea; Private DependModuleb Moduleb; Public DependModulea getModulea () {return modulea; } public void setModulea (DependModulea modulea) {this.modulea = modulea; } public dependModuleb getModuleb () {return moduleb; } public void setModuleb (DependModuleb Moduleb) {this.moduleb = moduleb; } }interface DependModuleA{ public void funcFromModuleA();}interface DependModuleB{ public void funcFromModuleB();}class DependModuleAImpl implements DependModuleA{ @Override public void funcFromModuleA() { System.out.println("This is func from Module A"); }} kelas DependModulebImpl mengimplementasikan DependModuleb {@Override public void funcfrommoduleB () {System.out.println ("Ini FUNC dari Modul B"); }}Jika kita tidak mengadopsi IOC, tetapi mengandalkan modul utama itu sendiri untuk mengontrol penciptaan modul dependennya, maka itu akan seperti ini:
Public Class SimpleOcDemo {public static void main (String [] args) melempar ClassNotFoundException {mainModule mainModule = new MainModule (); MainModule.setModulea (baru DependModuleAmpl ()); MainModule.setModuleb (baru DependModulebImpl ()); MainModule.getModulea (). FuncfromdomuleA (); MainModule.getModuleb (). FuncfrommoduleB (); }}Ini adalah definisi prototipe wadah IOC kami yang disederhanakan. Ketika wadah diinisialisasi setelah startup, ia akan membaca file konfigurasi yang ditulis oleh pengguna. Di sini kami mengambil file konfigurasi properti sederhana sebagai contoh. Hanya ketika pengguna memanggil metode Getbean, kacang yang sesuai akan benar -benar dirakit dan dimuat sesuai dengan file konfigurasi. Peta yang digunakan untuk menyimpan kacang yang dirakit dipertahankan di dalam prototipe wadah yang kami tentukan. Jika ada kacang yang memenuhi persyaratan, itu tidak perlu dibuat lagi:
Class SimpleOcContainer {Private Properties Properties = New Properties (); peta pribadi <string, objek> modulemap = hashmap baru <> (); {coba {properties.load (FileInputStream baru (file baru ("Simpleioc.Properties"))); } catch (Exception e) {E.PrintStackTrace (); }} objek publik getBean (string moduleName) melempar classNotFoundException {objek instanceObj; if (modulemap.get (moduleName)! = null) {system.out.println ("return old bean"); return modulemap.get (modulename); } System.out.println ("Buat kacang baru"); String fullClassName = properties.getProperty (Modulename); if (fullclassName == null) lempar classnotfoundException baru (); else {class <? memperluas objek> clazz = class.forname (fullclassName); coba {instanceObj = clazz.newInstance (); instanceObj = buildattachedModules (modulename, instanceObj); modulemap.put (Modulename, instanceObj); kembalikan instanceobj; } catch (InstantiationException e) {E.PrintStackTrace (); } catch (ilegalAccessException e) {e.printstacktrace (); }} return null; } Private Object BuildAttachedModules (String ModuleName, Object InstanceObj) {Set <String> propertiesKeys = Properties.StringPropertyNames (); Bidang [] bidang = instanceObj.getClass (). GetDecledFields (); untuk (key string: propertieskeys) {if (key.contains (moduleName) &&! key.equals (moduleName)) {coba {class <? Extends Object> clazz = class.forname (properties.getProperty (properties.getProperty (key)))); untuk (bidang bidang: bidang) {if (field.getType (). isAssignableFrom (clazz)) field.set (instanceObj, clazz.newinstance ()); }} catch (Exception e) {e.printstacktrace (); }}} return instanceObj; }}Ini adalah file konfigurasi ketergantungan yang kami tulis menggunakan file konfigurasi properti. File konfigurasi ini adalah "gambar" dari modul perakitan kami. Sintaks di sini sepenuhnya ditentukan oleh kami. Dalam wadah IOC musim semi nyata, untuk mengekspresikan logika ketergantungan yang lebih kompleks, file konfigurasi format XML yang lebih berkembang atau konfigurasi anotasi yang lebih baru akan digunakan, dan prosesor anotasi akan digunakan untuk menyelesaikan analisis gambar:
MainModule = com.rocking.demo.mainmodulemainmodule.modulea = ModuleAMAinModule.moduleb = ModulebModulea = com.rocking.demo.dependModuleAmplModuleb = com.rocking.demo.dependModule
Ini adalah kode uji. Dapat dilihat bahwa kita dapat sepenuhnya memperoleh modul yang memenuhi persyaratan melalui wadah IOC yang kami tentukan. Pada saat yang sama, kami juga dapat menemukan bahwa wadah yang kami tentukan dapat mempertahankan kacang ini untuk kami. Ketika kacang telah dikumpulkan dan dibuat, itu tidak perlu dibuat lagi.
Public Class SimpleOcDemo {public static void main (String [] args) melempar ClassNotFoundException {SimpleiocContainer container = new SedemedIocContainer (); DependModulea modulea = (DependModulea) container.getBean ("Modulea"); MODULEA.FUNCFOMPOMEDULEA (); DependModuleb Moduleb = (DependModuleb) container.getBean ("Moduleb"); MODULEB.FUNCFROMTYEB (); MainModule MainModule = (MainModule) container.getBean ("MainModule"); MainModule.getModulea (). FuncfromdomuleA (); MainModule.getModuleb (). FuncfrommoduleB (); container.getbean ("MainModule"); }}Ini adalah prototipe wadah IOC yang saya buat berdasarkan ide dasar IOC. Meskipun Spring IOC memiliki sintaks yang kompleks, tugas-tugas yang diselesaikan pada akhirnya adalah sama pada intinya, yang disebut "semua perubahan tidak akan dipisahkan dari esensi mereka."
Proses spesifik Spring IOC
Terakhir kali, prototipe implementasi umum IOC ditampilkan. Jadi bagaimana cara mengimplementasikan proses pemuatan POJOS secara spesifik dalam kerangka musim semi wadah ini berdasarkan konfigurasi informasi meta metadata? Ada banyak tempat di seluruh proses kerja kontainer IOC musim semi yang dirancang agar cukup fleksibel, memberikan banyak ruang bagi pengguna untuk menyelesaikan tugas mereka sendiri, daripada hanya menyelesaikan proses mekanis wadah.
Ini adalah diagram proses dari seluruh proses kerja kontainer IOC:
1. Tahap Startup Kontainer (1) Memuat Informasi File Konfigurasi (2) Menganalisis Informasi File Konfigurasi (3) Perakitan Beandefinition
(4) Pasca pemrosesan pertama, meta-informasi seperti file konfigurasi atau anotasi dan informasi kelas JavaBean dimuat ke dalam wadah IOC. Wadah membaca file konfigurasi format XML. File konfigurasi ini adalah ketergantungan yang dinyatakan oleh pengguna dan perakitan yang membutuhkan perhatian khusus. Ini adalah "gambar eksternal" awal untuk merakit kacang. Mesin parsing dalam wadah dapat mengurai karakter meta-informasi dalam bentuk teks yang kita tulis ke dalam definisi beand yang dapat dikenali di dalam wadah, yang dapat memahami definisi beand. Itu menjadi struktur kelas yang mirip dengan mekanisme refleksi. Beandefinition ini diperoleh dengan menganalisis JavaBeans dan file konfigurasi memperoleh struktur dasar merakit JavaBean yang memenuhi persyaratan. Jika Anda perlu memodifikasi beandefinition selain beandefinition, pasca-pemrosesan ini dilakukan. Pasca pemrosesan umumnya diproses melalui BeanFactoryPostProcessor dalam kerangka musim semi.
Kami masih menggunakan contoh -contoh yang kami gunakan terakhir kali untuk menggambarkan prinsip pengoperasian beandefinition ini: ada tiga kacang, modul utama Mainmodule dan modul ketergantungan tergantung pada Modulea dan DependModuleb. Yang pertama tergantung pada dua modul terakhir. Dalam file konfigurasi, kami umumnya mendeklarasikan dependensi seperti ini:
<? XML Versi = "1.0" encoding = "utf-8"?> <beans xmlns = "http://www.springframework.org/schema/beans" xmlns: xsi = "http://www.w3.org/2001/xmls xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="mainModule"> <property name="moduleA"> <ref bean="moduleA"/> </property> <name properti = "Moduleb"> <ref bean = "moduleb"/> </pruptent> </ bean> <bean id = "modulea"> </ bean> <bean id = "moduleb"> </ bean> </tean>
Ini adalah program kami yang menunjukkan perakitan wadah beanfactory standar (salah satu implementasi wadah IOC pegas) ke file konfigurasi di atas:
kelas MainModule {private DependModulea modulea; Private DependModuleb Moduleb; Public DependModulea getModulea () {return modulea; } public void setModulea (DependModulea modulea) {this.modulea = modulea; } public dependModuleb getModuleb () {return moduleb; } public void setModuleb (DependModuleb Moduleb) {this.moduleb = moduleb; }}interface DependModuleA { public void funcFromModuleA();}interface DependModuleB { public void funcFromModuleB();}class DependModuleAImpl implements DependModuleA { @Override public void funcFromModuleA() { System.out.println("This is func from Module A"); }} kelas DependModulebImpl mengimplementasikan DependModuleb {@Override public void funcfrommoduleB () {System.out.println ("Ini FUNC dari Modul B"); }} Public Class PublicioCdemo {public static void main (String [] args) melempar classNotFoundException {defaultListableBeanFactory beanfactory = new DefaultListableBeanFactory (); XmlbeandefinitionReader reader = xmlbeandefinitionReader baru (beanfactory); reader.loadbeandefinitions ("beans.xml"); MainModule mainModule = (mainModule) beanfactory.getbean ("mainModule"); MainModule.getModulea (). FuncfromdomuleA (); MainModule.getModuleb (). FuncfrommoduleB (); }}Di sini file konfigurasi dan JavaBean kami dimuat dan dibaca dan diuraikan. Di sini generasi beandefinition dan proses penggunaan disembunyikan di dalamnya. Ini adalah proses umum yang benar -benar terjadi di dalam IOC:
Public Class SimpleOcDemo {public static void main (String [] args) melempar ClassNotFoundException {defaultListableBeanFactory beanFactory = new DefaultListableBeanFactory (); AbstractBeandefinition MainModule = rootbeandefinition baru (MainModule.class); AbstractBeandefinition modulea = rootbeandefinition baru (DependModuleAmpl.class); AbstractBeandefinition moduleb = rootBeandefinition baru (DependModulebImpl.class); beanfactory.registerbeandefinition ("MainModule", MainModule); beanfactory.registerbeandefinition ("Modulea", Modulea); beanfactory.registerbeandefinition ("moduleb", moduleb); MutablePropertyValues PropertyValues = New MutablePropertyValues (); PropertyValues.Add ("Modulea", Modulea); PropertyValues.Add ("Moduleb", Moduleb); MainModule.SetPropertyValues (PropertyValues); Modul MainModule = (MainModule) beanfactory.getBean ("MainModule"); module.getModulea (). funcfromodulea (); module.getModuleb (). funcfrommoduleB (); }}Setelah memuat dan membaca informasi meta XML, mesin parsing IOC akan membuat modul yang disebutkan di dalamnya menjadi beandefinition berdasarkan jenis sebenarnya. Lefinisi beand ini dapat dianggap sebagai proses refleksi atau proxy. Tujuannya adalah untuk membuat wadah IOC menghapus struktur kacang dari objek instan yang akan dibuat di masa depan, dan kemudian mendaftarkan struktur kacang ini ke dalam beanfactory, dan kemudian menambahkan dependensi modul utama ke sifat -sifat modul utama dalam bentuk injeksi setter (ini tergantung pada definisi dari semua metode setter atau metode inisialisasi yang disediakan oleh modul utama.) sudah terbentuk. Setelah itu, panggil saja metode Getbean untuk menghasilkan kacang yang memenuhi persyaratan. Ini adalah tahap proses berikutnya, dan kami akan membicarakannya nanti.
Setelah mendaftarkan informasi tentang "menggambar" beandefinition ke beanfactory, kita masih bisa membuat perubahan pada beandefinition terdaftar. Ini adalah salah satu aspek fleksibel dari desain musim semi untuk pengguna yang disebutkan sebelumnya. Itu tidak berarti bahwa semua proses tidak terkendali, tetapi menyisakan banyak ruang bagi pengguna untuk bermain di banyak tempat. Metode spesifik adalah menggunakan prosesor beanfactory beanfactoryprocessor untuk campur tangan dalam pemrosesan beanfactory untuk lebih menulis ulang bagian seefinisi beandfinition yang perlu kita ubah. Proses ini sesuai dengan proses "pasca pemrosesan" dalam proses.
Mengambil salah satu prosesor umum: Atribut prosesor konfigurasi placeholder sebagai contoh, ini adalah untuk memproses beanfactory terdaftar setelah dibangun, sehingga konten dalam atribut beandefinition yang sesuai dimodifikasi ke informasi dalam prosesor konfigurasi yang ditentukan file konfigurasi:
DefaultListableBeanFactory beanfactory = new DefaultListableBeanFactory (); xmlbeandefinitionReader reader = new XmlBeandefinitionReader (BeanFactory); pembaca. PropertyplaceHolderConfigurer (); configurer.setLocation (classpathResource baru ("about.properties")); configurer.postprocessBeanFactory (beanfactory);The BeanfactoryPostProcessor akan memproses beanfactory. Hasilnya adalah mengubah beberapa atribut yang didefinisikan dalam seefinisi beand ke beberapa informasi di lokasi BeanfactoryPostProcessor.
2. Tahap Instantiasi Kacang
Di bawah bimbingan "gambar internal" yang diproses dari beandefinition, wadah selanjutnya dapat mengubah kebiasaan beanden menjadi objek instance yang diaktifkan yang ada dalam memori melalui refleksi atau produksi bytecode dinamis CGLIB, dan kemudian merakit objek ketergantungan yang ditentukan oleh peminat beand ke dalam objek instance yang baru dibuat melalui pemecatan atau pemecatan inisialisasi. Di sini, referensi objek ketergantungan sebenarnya ditetapkan ke atribut objek yang perlu diandalkan.
Tetapi harus dicatat di sini bahwa instance yang dibuat bukan hanya contoh sederhana dari definisi kacang, tetapi contoh beanwrapper yang dibungkus oleh Spring. Mengapa harus digunakan untuk membungkus kacang dengan metode beanwrapper? Karena Beanwrapper menyediakan antarmuka untuk mengakses properti kacang secara seragam. Setelah membuat kerangka kerja kacang dasar, properti di dalamnya harus diatur. Metode setter dari setiap kacang berbeda, sehingga akan sangat rumit jika Anda secara langsung mengaturnya dengan refleksi. Oleh karena itu, Spring menyediakan pembungkus ini untuk menyederhanakan pengaturan properti:
Beanwrapper beanwrapper = beanwrapperImpl baru (class.forname ("com.rocking.demo.mainmodule")); beanwrapper.setPropertyValue ("Modulea", class.forname ("com.rocking.demo.depmoduleAml"). "moduleB", Class.forName("com.rocking.demo.DepModuleBImpl").newInstance());MainModule mainModule= (MainModule) beanWrapper.getWrappedInstance();mainModule.getModuleA().funcFromA();mainModule.getModuleB().funcFromB();Proses di atas menunjukkan bahwa di musim semi, Anda dapat memahami struktur instance bean yang dibungkus di masa depan dengan mendapatkan wadah reflektif kelas dan membuat kemasan. Gunakan metode pengaturan properti terpadu setPropertyValue untuk mengatur properti untuk contoh paket ini. Contoh kacang terakhir yang diperoleh diperoleh melalui GetWrappedInstance, dan Anda dapat menemukan bahwa atributnya telah berhasil ditugaskan.
Pada saat ini, instance kacang sebenarnya benar -benar dapat digunakan, tetapi Spring juga menyiapkan strategi fleksibel bagi kami dalam tahap instantiasi untuk menyelesaikan intervensi pengguna pada tahap ini. Mirip dengan Kontrol Beandefinition Kontrol BeanFactoryPostessor pada tahap startup kontainer, selama tahap instantiasi, Spring menyediakan prosesor beanpostprocessor untuk beroperasi pada instance yang dirakit untuk menyelesaikan perubahan yang mungkin:
Berikut adalah contoh untuk mengilustrasikan bahwa Anda mendefinisikan kelas implementasi beanpostprocessor, mengimplementasikan metode postprocessafterinitialisasi dan postprocessBeforeInalization untuk mendefinisikan operasi yang dilakukan secara terpisah setelah dan sebelum perakitan instance bean. Setelah beanfactory menambahkan prosesor ini, setiap kali metode Getbean berkumpul, kedua metode tersebut akan dipanggil dalam instance kacang yang dirakit sesuai dengan "gambar" (termasuk instance yang tergantung pada proses perakitan). Metode ini dapat memodifikasi instance kacang ini.
Berikut adalah contoh seperti ini (MainModule dan ketergantungannya sama dengan yang sebelumnya dalam artikel ini):
kelas modulec {private string x; string publik getx () {return x; } public void setx (string x) {this.x = x; }} kelas ModulePostProcessor mengimplementasikan BeanPostProcessor {@Override Objek publik PostProcessAntInitialization (Object Object, String String) melempar BeansException {System.out.println (string); if (objek instance dari modulec) {system.out.println (string); ((Modulec) objek) .setx ("setelah"); } return objek; } @Override Objek publik PostProcessBeforeInitialization (Object Object, String String) melempar beansException {if (objek instance dari modulec) {((modulec) objek) .setx ("sebelum"); } return objek; }} kelas publik sangat sederhana {public static void main (string [] args) melempar classnotfoundException, beansException, InstantiationException, ilegalAccessException {defaultListableBeanFactory beanFactory = new defaultListableBeanFactory (); XmlbeandefinitionReader reader = xmlbeandefinitionReader baru (beanfactory); reader.loadBeandefinitions (classpathResource baru ("beans.xml")); Modulepostprocessor postprocessor = ModulePostProcessor () baru; beanfactory.addbeanpostprocessor (postprocessor); Modul MainModule = (MainModule) beanfactory.getBean ("MainModule"); Modulec modulec = (modulec) beanfactory.getBean ("modulec"); System.out.println (modulec.getX ()); }}Ini adalah file konfigurasi ketergantungan untuk kacang:
<? XML Versi = "1.0" encoding = "utf-8"?> <beans xmlns = "http://www.springframework.org/schema/beans" xmlns: xsi = "http://www.w3.org/2001/xmls XSI: schemalocation = "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans http://www.spramework.org/schema/beans/beans" aftppring.spramework.org/schema/beans name = "Modulea"> <ref bean = "modulea"/> </prop Property name = "Moduleb"> <ref bean = "moduleb"/> </propert> </ bean> <bean id = "modulea"> <property name = "infoa"> <value> $ {modulea.infoa} </property> <poera "> <value> $ {Modulea.infoa} </value <" name = "infob"> <value> Info moduleb </ value> </pruptent> </tact> <bean id = "Modulec"> </ bean> </tean>Dari hasil akhir, kita dapat melihat bahwa setiap kali instance Getbean (termasuk yang dihasilkan oleh dependensi) yang diperoleh dengan memanggil metode Getbean akan diambil oleh BeanpostProcessor untuk pra dan pasca pemrosesan.
Selain memproses kacang rakitan yang mirip dengan beanpostprocessor di atas, Spring juga dapat menetapkan fungsi panggilan balik untuk proses inisialisasi dan penghancuran kacang dengan mengonfigurasi metode init dan destroy-metode. Fungsi panggilan balik ini juga dapat secara fleksibel memberikan kesempatan untuk mengubah instance kacang.
Seluruh proses IOC musim semi pada dasarnya sama dengan prototipe IOC yang kami tulis sendiri, kecuali bahwa desain yang kompleks memungkinkan proses IOC untuk memberi pengguna ruang yang lebih fleksibel dan efektif. Selain itu, IOC Spring juga telah mencapai desain yang sangat indah dalam hal keamanan, stabilitas kontainer, dan metadata untuk efisiensi konversi kacang, membuat fondasi IOC, wadah musim semi, stabil.