1. Pendahuluan
Kita tahu bahwa musim semi bisa malas memuat, yaitu, instantiate kacang saat itu benar -benar digunakan. Tentu saja, ini bukan masalahnya. Misalnya, mengkonfigurasi properti malas-init dari kacang dapat mengontrol waktu pemuatan pegas. Sekarang kinerja, memori, dll. Mesin relatif tinggi, dan pada dasarnya tidak menggunakan pemuatan malas. Kacang dimuat pada awal wadah, dan waktu startup sedikit lebih lama. Dengan cara ini, ketika kacang sebenarnya diperoleh untuk penggunaan bisnis, banyak beban dapat dikurangi. Ini akan dianalisis nanti. Ketika kita menggunakan kacang, cara yang paling langsung adalah mendapatkannya dari Factroy, yang merupakan sumber instance pemuatan kacang.
Baru -baru ini, saya mengalami masalah aneh ketika mengerjakan proyek, yaitu, keakuratan injeksi ketergantungan kacang terkait dengan urutan injeksi kacang langsung, tetapi dalam keadaan normal tidak ada hubungannya dengan pesanan. Jika Anda sedang terburu -buru, izinkan saya memberi tahu Anda satu per satu.
2. Ketergantungan Siklik Kacang biasa-Tidak terkait dengan urutan injeksi
2.1 Contoh dan prinsip ketergantungan melingkar
Public Class Beana {private beanb beanb; public beanb getBeanB () {return beanb;} public void setBeanB (beanb beanb) {this.beanb = beanb;}} Public Class BeanB {private beana beana; publik beana getbeana () {return beana;} public void setBeana (beana beana) {this.beana = beana;}}<bean id = "beana"> <name properti = "beanb"> <ref bean = "beanb"/> </prop Propert> </ bean>
<bean id = "beanb"> <name properti = "beana"> <ref bean = "beana"/> </prop Property> </bean>
Injeksi ketergantungan melingkar di atas berfungsi secara normal karena musim semi menyediakan fungsi referensi awal. Pertama, ada peta bersamaan bernama SingletOnObjects di Spring untuk menyimpan semua kacang yang inisialisasi dan diinisialisasi, sementara singletonfactories digunakan untuk menyimpan informasi kacang (Beanname, dan pabrik panggilan balik) yang perlu diselesaikan. Ketika instantiating beana, getBean(“beanA”); Pertama, lihat apakah ada Beana di singletonObjects, itu akan kembali:
(1)
Object sharedInstance = getSingleton(beanName);//getSingleton(beanName,true);if (sharedInstance != null && args == null) {if (logger.isDebugEnabled()) { if (isSingletonCurrentlyInCreation(beanName)) { logger.debug("Returning eagerly cached instance of singleton bean '" + beanname +"' yang belum sepenuhnya diinisialisasi - konsekuensi dari referensi melingkar "); } else {logger.debug ("instance pengembalian singleton bean '" + beanname + "'"); }} // Jika itu adalah kacang normal, ia mengembalikan sharedInstance.getObject (); bean = getObjectForbeanInstance (sharedInstance, name, beanname, null);} objek yang dilindungi getseTon (string beanname, boolean weolylyreference) {objek singletonobject = this.singletonobjects.get (beanname); if (singletonObject == null) {disinkronkan (this.singletonObjects) {singletonObject = this.earlysingletonObjects.get (beanname); if (singletonObject == null && owolearlyreference) {objectFactory singletonfactory = (objectFactory) this.singletonfactory.get (beanname); if (singletonfactory! = null) {singletonObject = singletonfactory.getObject (); this.earlysingletonObjects.put (beanname, singletonObject); this.singletonfactory.remove (beanname); }}} return (singletonObject! = null_object? singletonObject: null);} Pada awalnya, pasti tidak ada beana, jadi jika allowCircularReferences=true diatur (default benar) dan kacang saat ini adalah bagian tunggal dan kacang saat ini sedang dibuat, kemudian sebelum menginisialisasi atribut, masukkan informasi kacang ke dalam peta single-piece singletonfactory singletonfactory:
(2)
boolean EarlySingLeTonExPosure = (mbd.issingleton () && this.allowcircularReferences && issingletonCureCreation (beanname));
if (earlySingletonExposure) {if (logger.isDebugEnabled()) { logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references");}addSingletonFactory(beanName, new ObjectFactory() { public Object getObject() throws BeansException { return getearlybeanreference (beanname, mbd, bean);}});} void yang dilindungi addsingletonfactory (string beanname, objekfactory singletonfactory) {assert.notnull (singletonfactory, "singleton factory tidak boleh null"); disinkronkan (this.singletonObjects) {if (! this.singletonobjects.containskey (neannneNkey (neanNnnEnkey (neanNnnEnkey (nnnnneNkey (containskey) {if (! this.singletonfactory.put (beanname, singletonfactory); this.earlysingletonobjects.remove (beanname); this.registeredsingletons.add (beanname); }}} Kemudian suntikkan beanB ke atribut instance. Saat menyuntikkan atribut, getBean(“beanB”) dan menemukan bahwa beanb tidak ada di singletonobjects, itu akan membuat instantiasi beanb, kemudian meletakkan singletonfactories, kemudian menyuntikkan beana, dan kemudian memicu getBean(“beanA”); Pada saat ini, Gotingleton akan mengembalikan Beana yang sedang dipakai. Setelah beanb diinisialisasi, tambahkan beanb ke singletonobjects dan kemudian kembali, kemudian beana diinisialisasi, tambahkan beana ke singletonobjects dan kemudian kembali
2.2 sakelar yang memungkinkan dependensi loop
Public Class TestCircle2 {private final static classpathxmlapplicationContext moduleContext; uji uji statis privat; static {moduleContext = new ClassPathXMLapPlicationContext (string baru [] {"beans-circile.xml"}); MODULECONTEXT.SetAllOWCircularReferences (false); test = (test) moduleContext.getBean ("test");} public static void main (string [] args) {System.out.println (test.name);}}Ada properti Allow CircularReferences di kelas ClassPathXMLapPlicationContext untuk mengontrol apakah dependensi melingkar diizinkan untuk benar secara default. Setelah mengaturnya menjadi false, ditemukan bahwa dependensi melingkar masih dapat berjalan secara normal. Lihat kode sumbernya:
public classpathxmlapplicationContext (string [] configLocations) melempar beansException {this (configLocations, true, null);} Public ClassPathXMLApplicationContext (String [] ConfigLocations, Boolean Refresh, ApplicationContext Parent) melempar BeansException {super (Parent); setConfigLocations (configLocations); if (refresh) {refresh ();}} Public ClassPathXMLApplicationContext (String [] ConfigLocations, Boolean Refresh, ApplicationContext Parent) melempar BeansException {super (Parent); setConfigLocations (configLocations); if (refresh) {refresh ();}} Ketahuilah bahwa wadah akan disegarkan ketika ClassPathXMLapPlicationContext dibangun secara default.
Metode Refresh akan memanggil RefreshBeanFactory:
final void final refreshbeanfactory () melempar beansException {if (hasbeanfactory ()) {dasharbeans (); closeBeanFactory ();} coba {// Buat Bean Factory defaultListableBeanFactory beanFactory = createBeanFactory (); // Kustomisasi Properti Pabrik Bean CustomizeBeanFactory (BeanFactory); loadbeandefinitions (beanfactory); disinkronkan (this.beanFactoryMonitor) {this.beanFactory = beanfactory; }} catch (ioException ex) {throw new ApplicationContextException ("I/O Error Parsing dokumen XML untuk konteks aplikasi [" + getDisplayName () + "]", ex);}} void customizeBeanFactory (defaultListableBeanFactory beanfactory) {if (this.allowbeandefinitionriding! = null) {beanfactory.setallowbeanDefinitionriding (this.allowbeanDefinitionRiding.boolaeanValue () (); ini. beanfactory.setallowcircularReferences (this.allowcircularReferences.BooleanValue ());}} Anda akan tahu di sini bahwa sebelum kita memanggil moduleContext.setAllowCircularReferences(false) , CustomizeEnfactory dari CustomizeBeanFactory yang ditinggalkan oleh Spring telah dieksekusi. Alasan terakhir adalah bahwa sebelum memanggil pengaturan, pabrik kacang telah disegarkan, sehingga kode uji diubah menjadi:
public class TestCircle {private final static ClassPathXmlApplicationContext moduleContext;private static Test test;static { //Initialize the container context, but do not refresh the container moduleContext = new ClassPathXmlApplicationContext(new String[]{"beans-circile.xml"},false); MODULECONTEXT.SetAllOWCircularReferences (false); // Segarkan wadah modulecontext.refresh (); test = (test) moduleContext.getBean ("test");} public static void main (string [] args) {System.out.println (test.name);}}Sekarang tes akan melempar pengecualian:
Disebabkan oleh: org.springframework.beans.factory.beancreationException: kesalahan membuat kacang dengan nama 'beana' yang didefinisikan dalam sumber daya jalur kelas [bean-circile.xml]: tidak dapat menyelesaikan referensi untuk bean 'beanb' saat menetapkan properti bean 'beanb'; Pengecualian bersarang adalah org.springframework.beans.factory.beancreationexception: kesalahan membuat kacang dengan nama 'beanb' yang didefinisikan dalam sumber daya jalur kelas [bean-circile.xml]: tidak dapat menyelesaikan referensi untuk kacang 'beana' saat menetapkan properti kacang 'beana'; Pengecualian bersarang adalah org.springframework.beans.factory.beanCure -increationException: kesalahan membuat kacang dengan nama 'beana': Bean yang diminta saat ini dalam penciptaan: apakah ada referensi melingkar yang tidak dapat diselesaikan?
3. Kacang Pabrik dan Ketergantungan Siklik Kacang Biasa - Terkait dengan Pesanan Injeksi
3.1 Kode Uji
Kacang pabrik
public class MyFactoryBean implements FactoryBean,InitializingBean{private String name;private Test test;public String getName() { return name;}public void setName(String name) { this.name = name;}public DependentBean getDepentBean() { return dependentBean;}public void setDepentBean(DependentBean dependentBean) { this.dependentBean = DependentBean;} Private DependentBean DependentBean; Objek Publik GetObject () melempar Exception {return test;} kelas publik getObjectType () {// todo Metode yang dihasilkan otomatis test return return.class;} public boolean issingleton () {// TODO Metode stub rebus retir true;} public {{{{// TODO Metode Stub True-Generated Metode True;} public {{{{{// TODO Metode Stub True-Generated Metode Stub True;} public {{{{{{// TODO Metode Stub True-Generated Metode True;} Public {{{{{// TODO Metode Stub True; System.out.println ("Nama:" + this.name); tes = tes baru (); test.name = dependentBean.dosomething () + this.name;}} Untuk kesederhanaan, cukup tulis variabel publik
tes kelas publik {nama string publik;} Public Class DependentBean {public String dosomething () {return "Hello:";}@AutoWiredPrivate Test Test;} Konfigurasi XML
<bean id = "test"> <properti name = "dependentBean"> <bean> </tact> </prop Property> <properti name = "name" value = "zlx"> </property> </ bean>
Fungsi MyFactoryBean kacang pabrik adalah untuk membungkus kelas uji. Pertama, atur properti untuk myfactorybean, kemudian buat instance tes dalam metode afterpropertiesset dari myfactorybean dan atur properti. Instantiating MyFactoryBean pada akhirnya akan memanggil metode GetObject untuk mengembalikan objek uji yang dibuat. Di sini myfactorybean tergantung pada depentbean, dan dependentbean itu sendiri tergantung pada tes, jadi ini adalah ketergantungan melingkar
tes:
Public Class TestCircle2 {private final static classpathxmlapplicationContext moduleContext; uji uji statis privat; static {moduleContext = new ClassPathXMLapPlicationContext (string baru [] {"beans-circile.xml"}); test = (test) moduleContext.getBean ("test");} public static void main (string [] args) {System.out.println (test.name);}}hasil:
Disebabkan oleh: org.springframework.beans.factory.beanscreationException: kesalahan membuat kacang dengan nama 'com.alibaba.test.circle.dependentbean#1C701A27': Autowiring bidang gagal; Pengecualian bersarang adalah org.springframework.beans.factory.beanscreationException: Tidak dapat Autowire Field: Private com.alibaba.test.circle.test com.alibaba.test.circle.dependentbean.test; Pengecualian bersarang adalah org.springframework.beans.factory.beanCure -increationException: kesalahan membuat kacang dengan nama 'tes': factorybean yang saat ini dalam penciptaan kembali nol dari getObject
3.2 Analisis Alasan
Saat tes instantiating getBean(“test”) akan dipicu, dan itu akan melihat apakah ada kacang saat ini
Jika tidak ada, buat instance tes. Setelah pembuatan, informasi kacang saat ini akan ditempatkan di peta single-piece singletonfactory.
Kemudian suntikkan ketergantungan atribut ke dalam instance. Ketika injeksi atribut digunakan, getBean(“depentBean”) akan digunakan.
Jika Anda menemukan bahwa DependentBean tidak ada, Anda akan membuat instantiasi dependentbean dan kemudian memasukkannya ke dalam singletonfactories.
Kemudian tes injeksi autowired, dan kemudian getBean(“test”); Saat ini (1) Getingleton mengembalikan tes instantiated. Karena tes adalah kacang pabrik, return test.getObject();
MyFactoryBean Afterpropertiesset belum dipanggil, jadi test.getObject() mengembalikan nol.
Berikut ini adalah proses penciptaan kacang musim semi berikut:
getBean ()-> Buat instance-> AutoWirired-> Set Property-> AfterPropertiesset
Artinya, menyebut metode GetObject dipanggil lebih awal dari metode afterpropertiesset.
Lalu mari kita ubah myfactorybean menjadi sebagai berikut:
Objek publik getObject () melempar Exception {// TODO Metode yang dihasilkan otomatis Stubif (null == test) {afterpropertiesset ();} return test;} public void afterpropertiesset () melempar Exception {if (null == test) {System.out.println ("name:" + this.name); tes = tes baru (); test.name = dependentBean.dosomething () + this.name;}} Artinya, jika Anda menilai di dalam getObject terlebih dahulu, lebih baik untuk test==null Kemudian hubungi AfterPropertiesset, dan kemudian jika test==null membuat instance tes di dalam afterpropertiesset, itu terlihat bagus, dan saya benar -benar ingin menyelesaikan masalah kita. Tetapi pada kenyataannya, itu masih tidak berhasil, karena Afterpropertiesset menggunakan DependentBean secara internal, dan pada saat ini depentBean=null .
3.3 Berpikir Tentang Cara Memecahkannya
3.2 Alasan analisis adalah bahwa myfactorybean pertama kali dibuat, dan depentbean diciptakan selama proses menciptakan myfactorybean. Saat membuat depentbean, instance dari myfactorybean autowired diperlukan. Kemudian, metode GetObject dipanggil sebelum menelepon AfterProPertiesset, jadi NULL dikembalikan.
Jadi, jika Anda membuat depentbean terlebih dahulu, dan kemudian buat myfactorybean? Analisis berikut dibuat:
Pertama, depentbean akan dipakai dan ditambahkan ke singletonfactories
Instance depentBean akan menguji autowired, sehingga instance tes akan dibuat terlebih dahulu.
Buat instance tes dan kemudian bergabung dengan singletonfactories
Instance tes akan menyuntikkan instance depentBean di atribut, jadi itu akan getBean(“depentBean”);
getBean(“depentBean”) menemukan bahwa sudah ada dependentbean dalam singletonfactories, dan mengembalikan objek dependentBean
Karena DependentBean bukan kacang pabrik, ia secara langsung mengembalikan ketergantungan
Test Instance akan disuntikkan ke instance depentbean berhasil, inisialisasi instalisasi tes ok
Instance depentbean instance tes autowired ok
Menurut analisis ini, layak untuk membuat depentbean terlebih dahulu, dan kemudian instantiate myfactorybean. Ubah XML ke yang berikut:
<bean id = "dependentBean"> </ bean> <bean id = "test"> <name properti = "dependentBean"> <ref bean = "dependentBean"/> </property> <properti name = "name" value = "zlx"> </properti> </ bean>
Hasil uji coba:
Nama: Zlx
Halo: Zlx
Jika benar -benar OK, maka menurut analisis ini, jika konfigurasi XML di atas disesuaikan, itu pasti akan membuat kesalahan, karena tes dibuat lebih awal dari dependentBean, dan ini benar setelah tes. Selain itu, dapat dibayangkan bahwa ketika kacang pabrik bergantung pada kacang pabrik, itu pasti akan gagal terlepas dari urutan deklarasi.
3.3 Pikiran
Yang di atas pertama menyuntikkan ketergantungan yang perlu digunakan di myfactorybean, dan kemudian menyuntikkan myfactorybean, dan masalahnya terpecahkan. Jadi jika Anda perlu menggunakan objek yang dibuat dengan id = "test" di kacang lain, bagaimana seharusnya kacang ini disuntikkan?
Apakah itu akan berhasil dengan cara yang sama? Tinggalkan agar semua orang berpikir ^^
kelas publik usetest {@autowiredprivate test test;}<bean id = "useTest"> </ bean> <bean id = "dependentBean"> </ bean> <bean id = "test"> <properti nama = "dependentBean"> <ref bean = "dependentBean"/> </properti name = "name" value = "zlx"> </properti> </bean>
4. Ringkasan
Ketika kacang biasa saling bergantung, urutan injeksi kacang tidak terkait, tetapi ketika kacang pabrik dan kacang biasa saling bergantung, kacang biasa harus dipakai terlebih dahulu. Ini karena kekhasan kacang pabrik, yaitu, ia memiliki metode getObject.
Oke, di atas adalah seluruh konten artikel ini. Saya berharap konten artikel ini memiliki nilai referensi tertentu untuk studi atau pekerjaan semua orang. Jika Anda memiliki pertanyaan, Anda dapat meninggalkan pesan untuk berkomunikasi. Terima kasih atas dukungan Anda ke wulin.com.