1. คำนำ
เรารู้ว่าฤดูใบไม้ผลิอาจขี้เกียจที่จะโหลดนั่นคืออินสแตนซ์ถั่วเมื่อใช้จริง แน่นอนว่านี่ไม่ใช่กรณี ตัวอย่างเช่นการกำหนดค่าคุณสมบัติ Lazy-init ของถั่วสามารถควบคุมเวลาการโหลดของฤดูใบไม้ผลิ ตอนนี้ประสิทธิภาพหน่วยความจำ ฯลฯ ของเครื่องค่อนข้างสูงและโดยทั่วไปไม่ได้ใช้การโหลดขี้เกียจ ถั่วถูกโหลดที่จุดเริ่มต้นของคอนเทนเนอร์และเวลาเริ่มต้นจะนานขึ้นเล็กน้อย ด้วยวิธีนี้เมื่อถั่วได้รับจริงสำหรับการใช้งานธุรกิจสามารถลดภาระจำนวนมากได้ สิ่งนี้จะถูกวิเคราะห์ในภายหลัง เมื่อเราใช้ถั่ววิธีที่ตรงที่สุดคือการรับพวกเขาจาก Factroy ซึ่งเป็นแหล่งที่มาของการโหลดอินสแตนซ์ถั่ว
เมื่อเร็ว ๆ นี้ฉันพบปัญหาแปลก ๆ เมื่อทำงานในโครงการนั่นคือความแม่นยำของการฉีดพึ่งพาถั่วนั้นเกี่ยวข้องกับคำสั่งของการฉีดยาโดยตรง แต่ภายใต้สถานการณ์ปกติมันไม่มีอะไรเกี่ยวข้องกับคำสั่ง หากคุณรีบมาให้ฉันบอกคุณทีละคน
2. ถั่วลันเตาทั่วไปขึ้นอยู่กับการพึ่งพา-ไม่เกี่ยวข้องกับลำดับการฉีด
2.1 ตัวอย่างและหลักการพึ่งพาวงกลม
Public Class Beana {Private Beanb Beanb; Public Beanb GetBeanb () {return beanb;} โมฆะสาธารณะ setBeanb (Beanb Beanb) {this.beanb = Beanb;}}}} Public Class Beanb {Beana Private Beana; Public Beana GetBeana () {return Beana;} โมฆะสาธารณะ setbeana (Beana Beana) {this.beana = Beana;}}}}<bean id = "Beana"> <property name = "Beanb"> <ref bean = "Beanb"/> </porement> </ebean>
<bean id = "Beanb"> <property name = "Beana"> <ref bean = "Beana"/> </porement> </ebean>
การฉีดขึ้นอยู่กับการพึ่งพาแบบวงกลมข้างต้นทำงานตามปกติเนื่องจากสปริงให้ฟังก์ชั่นการอ้างอิงระยะแรก ครั้งแรกมีแผนที่พร้อมกันชื่อ SingletonObjects ในฤดูใบไม้ผลิเพื่อเก็บถั่วอินสแตนซ์และเริ่มต้นทั้งหมดในขณะที่ SingletonFactories ใช้เพื่อเก็บข้อมูลถั่ว (ชื่อถั่วและโรงงานโทรกลับ) ที่ต้องแก้ไข เมื่ออินสแตนซ์ Beana, getBean(“beanA”); ก่อนอื่นดูว่ามี Beana ใน SingletonObjects หรือไม่มันจะกลับมา:
(1)
Object SharedInstance = GetSingleton (Beanname); // Getingleton (Beanname, true); ถ้า (SharedInstance! = null && args == null) {ถ้า (logger.isdebugenabled () {if (issingletoncurrentyincreation (beanname) '" + Beanname +"' ที่ยังไม่ได้เริ่มต้นอย่างสมบูรณ์ - เป็นผลมาจากการอ้างอิงแบบวงกลม "); } else {logger.debug ("การส่งคืนอินสแตนซ์แคชของ Singleton Bean '" + Beanname + "'"); }} // ถ้าเป็นถั่วปกติมันจะส่งคืน SharedInstance.getObject (); Bean = getObjectForBeanInstance (SharedInstance, ชื่อ, Beanname, Null);} วัตถุที่ได้รับการป้องกัน GetSingleton (String Beanname, Boolean อนุญาตให้ใช้งานได้) {Object SingletonObject = this.singletonobjects.get (Beanname); if (singletonObject == null) {ซิงโครไนซ์ (this.singletonObjects) {singletonObject = this.earlysingletonobjects.get (beanname); if (singletonObject == null && lewlowlyReference) {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);} ในตอนแรกไม่มีถั่วแน่นอนดังนั้นหากอนุญาตให้ใช้งาน allowCircularReferences=true (ค่าเริ่มต้นเป็นจริง) และถั่วปัจจุบันเป็นชิ้นเดียวและกำลังถูกสร้างขึ้นในขณะนี้ก่อนที่จะเริ่มต้นแอตทริบิวต์ให้ใส่ข้อมูลถั่วลงในแผนที่เดี่ยวชิ้นเดียว:
(2)
บูลีน ournsingletonexposure = (mbd.issingleton () && this.allowcircularreferences && issingletoncurrentlyincreation (Beanname));
if (ournsingletonexposure) {ถ้า (logger.isdebugenabled ()) {logger.debug ("การแคชถั่วอย่างกระตือรือร้น '" + Beanname + "' เพื่อให้สามารถแก้ไขการอ้างอิงแบบวงกลมที่อาจเกิดขึ้นได้ GetearlyBeanReference (Beanname, MBD, Bean);}});} Void Protected AddsingletonFactory (String Beanname, ObjectFactory SingletonFactory) {assert.notnull (SingletonFactory, "Singleton Factory ต้องไม่เป็นโมฆะ"); synchronized (this.singletonobjects) {ถ้า SingletonFactory); this.earlysingletonobjects.remove (Beanname); this.registeredsingletons.add (Beanname); - จากนั้นฉีด Beanb ลงในแอตทริบิวต์อินสแตนซ์ เมื่อฉีดแอตทริบิวต์ getBean(“beanB”) และพบว่า Beanb ไม่ได้อยู่ใน Singletonobjects มันจะยกตัวอย่าง Beanb จากนั้นใส่ singletonfactories จากนั้นฉีด Beana แล้วเรียก getBean(“beanA”); ในเวลานี้ GetSingleton จะส่งคืน Beana อินสแตนซ์ หลังจากเริ่มต้น Beanb แล้วให้เพิ่ม Beanb ไปยัง Singletonobjects แล้วกลับมาจากนั้น Beana จะเริ่มต้นเพิ่ม beana ลงใน singletonobjects แล้วกลับมา
2.2 สวิตช์ที่อนุญาตการพึ่งพาวนซ้ำ
Public Class TestCircle2 {ส่วนตัวสุดท้าย classpathxmlapplicationContext moduleContext; การทดสอบแบบคงที่ส่วนตัว; คงที่ {moduleContext = ใหม่ classPathxMlApplicationContext (สตริงใหม่ [] {"Beans-circile.xml"}); moduleContext.setallowcircularreferences (false); ทดสอบ = (ทดสอบ) moduleContext.getBean ("ทดสอบ");} โมฆะคงที่สาธารณะหลัก (สตริง [] args) {system.out.println (test.name);}}มีคุณสมบัติที่อนุญาตให้ใช้งานในคลาส classPathxMlapplicationContext เพื่อควบคุมว่าการพึ่งพาแบบวงกลมได้รับอนุญาตให้เป็นจริงโดยค่าเริ่มต้นหรือไม่ หลังจากตั้งค่าเป็นเท็จพบว่าการพึ่งพาแบบวงกลมยังสามารถทำงานได้ตามปกติ ดูที่ซอร์สโค้ด:
Public ClassPathxMlApplicationContext (String [] การกำหนดค่า) พ่น beansexception {this (configlocations, true, null);} Public ClassPathxMlApplicationContext (String [] การกำหนดค่า, การรีเฟรชบูลีน, ApplicationContext Parent) พ่น beansexception {super (parent); setConfigLocations (configlocations); if (รีเฟรช) Public ClassPathxMlApplicationContext (String [] การกำหนดค่า, การรีเฟรชบูลีน, ApplicationContext Parent) พ่น beansexception {super (parent); setConfigLocations (configlocations); if (รีเฟรช) รู้ว่าคอนเทนเนอร์จะได้รับการรีเฟรชเมื่อ classPathxMlapplicationContext ถูกสร้างขึ้นโดยค่าเริ่มต้น
วิธีการรีเฟรชจะเรียก RefreshBeanFactory:
ได้รับการป้องกันขั้นสุดท้าย Void RefreshBeanFactory () พ่น beansexception {ถ้า (hasbeanfactory ()) {destroybeans (); CloseBeanFactory ();} ลอง {// สร้าง Bean Factory DefaultListableBeanFactory BeanFactory = CreateBeanFactory (); // ปรับแต่งคุณสมบัติของโรงงานถั่ว CustomizeBeanFactory (Beanfactory); LoadBeanDefinitions (beanfactory); ซิงโครไนซ์ (this.BeanFactoryMonitor) {this.beanfactory = beanfactory; }} catch (ioexception ex) {โยน applicationcontextexception ใหม่ ("I/O ข้อผิดพลาดการแยกวิเคราะห์เอกสาร XML สำหรับบริบทแอปพลิเคชัน [" + getDisplayName () + "]", ex);}} Void Void CustomizeBeanFactory (defaultlistableBeanFactory beanfactory) {ถ้า (this.allowbeandefinitionoverriding! = null) {beanfactory.setallowbeandefinitionoverriding (this.allowbeandefinitionoverriding beanfactory.setallowcircularreferences (this.allowcircularreferences.booleanvalue ());}} คุณจะรู้ที่นี่ว่าก่อนที่เราจะเรียก moduleContext.setAllowCircularReferences(false) , customizeBeanFactory ของ customizeBeanFactory ที่เหลืออยู่ในฤดูใบไม้ผลิได้ถูกดำเนินการ เหตุผลสุดท้ายคือก่อนที่จะเรียกการตั้งค่าโรงงานถั่วได้รีเฟรชดังนั้นรหัสทดสอบจะเปลี่ยนเป็น:
TestCircle คลาสสาธารณะ {ส่วนตัวสุดท้าย classPathxMlApplicationContext ModuleContext; การทดสอบแบบคงที่ส่วนตัว; คงที่ {// เริ่มต้นบริบทคอนเทนเนอร์ แต่อย่ารีเฟรชคอนเทนเนอร์โมเดิร์นเท็กซ์ = ใหม่ classPathxMlApplicationContext (สตริงใหม่ [] {"Beans-Circile.xml" moduleContext.setallowcircularreferences (false); // รีเฟรชคอนเทนเนอร์ modulecontext.refresh (); ทดสอบ = (ทดสอบ) moduleContext.getBean ("ทดสอบ");} โมฆะคงที่สาธารณะหลัก (สตริง [] args) {system.out.println (test.name);}}ตอนนี้การทดสอบจะโยนข้อยกเว้น:
เกิดจาก: org.springframework.beans.factory.beancreationexception: ข้อผิดพลาดการสร้างถั่วด้วยชื่อ 'Beana' ที่กำหนดไว้ในทรัพยากรเส้นทางของคลาส [Beans-circile.xml]: ไม่สามารถแก้ไขการอ้างอิงถึงถั่ว 'Beanb' ในขณะที่ตั้งค่าถั่ว 'Beanb'; ข้อยกเว้นที่ซ้อนกันคือ org.springframework.beans.factory.beancreationexception: ข้อผิดพลาดการสร้างถั่วด้วยชื่อ 'Beanb' ที่กำหนดไว้ในทรัพยากรเส้นทางของคลาส [Beans-circile.xml]: ไม่สามารถแก้ไขการอ้างอิงถึงถั่ว 'Beana' ในขณะที่ตั้งค่าถั่ว 'Beana'; ข้อยกเว้นที่ซ้อนกันคือ org.springframework.beans.factory.beancurrentyincreationexception: ข้อผิดพลาดการสร้างถั่วด้วยชื่อ 'Beana': การร้องขอถั่วกำลังอยู่ในการสร้าง: มีการอ้างอิงแบบวงกลมที่ไม่สามารถแก้ไขได้หรือไม่?
3. ถั่วโรงงานและถั่วธรรมดาพึ่งพาวงจร - เกี่ยวข้องกับคำสั่งฉีด
3.1 รหัสทดสอบ
ถั่วโรงงาน
คลาสสาธารณะ MyFactoryBean ใช้ FactoryBean, InitializingBean {ชื่อสตริงส่วนตัว; การทดสอบส่วนตัวการทดสอบส่วนตัว; สตริงสาธารณะ getName () {ชื่อคืน;} โมฆะสาธารณะ setName (ชื่อสตริง) {this.name = name;} Public InderentBean ขึ้นอยู่กับ;} ส่วนตัวขึ้นอยู่กับการพึ่งพาอาศัยกัน; วัตถุสาธารณะ getObject () โยนข้อยกเว้น {การทดสอบกลับ;} ชั้นเรียนสาธารณะ getObjectType () {// todo วิธีการที่สร้างขึ้นอัตโนมัติ stub test.class; System.out.println ("ชื่อ:" + this.name); ทดสอบ = การทดสอบใหม่ (); test.name = adpendentBean.dosomething () + this.name;}} เพื่อความเรียบง่ายเพียงแค่เขียนตัวแปรสาธารณะ
การทดสอบคลาสสาธารณะ {ชื่อสตริงสาธารณะ;} คลาสสาธารณะขึ้นอยู่กับ {public string dosomething () {return "hello:";}@autowiredprivate ทดสอบทดสอบ;} การกำหนดค่า XML
<bean id = "test"> <property name = "ขึ้นอยู่กับ"> <bean> </epean> </preffec
ฟังก์ชั่น MyFactoryBean จากโรงงานคือการปิดคลาสทดสอบ ขั้นแรกให้ตั้งค่าคุณสมบัติสำหรับ MyFactoryBean จากนั้นสร้างอินสแตนซ์การทดสอบในวิธี AfterPropertIesset ของ MyFactoryBean และตั้งค่าคุณสมบัติ อินสแตนซ์ MyFactoryBean ในที่สุดจะเรียกวิธีการ getObject เพื่อส่งคืนวัตถุทดสอบที่สร้างขึ้น ที่นี่ MyFactoryBean ขึ้นอยู่กับ DepentBean และขึ้นอยู่กับการทดสอบดังนั้นนี่คือการพึ่งพาเป็นวงกลม
ทดสอบ:
Public Class TestCircle2 {ส่วนตัวสุดท้าย classpathxmlapplicationContext moduleContext; การทดสอบแบบคงที่ส่วนตัว; คงที่ {moduleContext = ใหม่ classPathxMlApplicationContext (สตริงใหม่ [] {"Beans-circile.xml"}); ทดสอบ = (ทดสอบ) moduleContext.getBean ("ทดสอบ");} โมฆะคงที่สาธารณะหลัก (สตริง [] args) {system.out.println (test.name);}}ผลลัพธ์:
เกิดจาก: org.springframework.beans.factory.beancreationexception: ข้อผิดพลาดการสร้างถั่วด้วยชื่อ 'com.alibaba.test.circle.dependentbean#1C701A27': การไหลของฟิลด์อัตโนมัติล้มเหลว; ข้อยกเว้นที่ซ้อนกันคือ org.springframework.beans.factory.beancreationexception: ไม่สามารถ AutoWire Field: Private com.alibaba.test.circle.test com.alibaba.test.circle.dependentbean.test; ข้อยกเว้นที่ซ้อนกันคือ org.springframework.beans.factory.beancurrentlyincreationexception: ข้อผิดพลาดการสร้างถั่วด้วยชื่อ 'ทดสอบ': FactoryBean
3.2 การวิเคราะห์เหตุผล
เมื่อมีการทดสอบอินสแตนซ์ getBean(“test”) จะถูกเรียกใช้และจะดูว่าถั่วปัจจุบันมีอยู่จริง
หากไม่มีอยู่ให้สร้างอินสแตนซ์ของการทดสอบ หลังจากการสร้างข้อมูลถั่วปัจจุบันจะถูกวางไว้ในแผนที่ SingletonFactories ชิ้นเดียว
จากนั้นฉีดแอตทริบิวต์ขึ้นอยู่กับอินสแตนซ์ เมื่อมีการใช้การฉีดแอตทริบิวต์ getBean(“depentBean”) จะถูกนำมาใช้
หากคุณพบว่าไม่มีการพึ่งพาอาศัยกันอยู่คุณจะยกตัวอย่างแอนชั่นที่พึ่งพาอาศัยกันแล้วใส่ไว้ใน SingletonFactories
จากนั้นทดสอบการฉีดอัตโนมัติจากนั้น getBean(“test”); ในเวลานี้ (1) GetSingleton ส่งคืนการทดสอบแบบอินสแตนซ์ เนื่องจากการทดสอบเป็นถั่วโรงงาน test.getObject();
Afterpropertiesset ของ MyFactoryBean ยังไม่ได้รับการเรียกดังนั้น test.getObject() ส่งคืน NULL
ต่อไปนี้เป็นกระบวนการสร้างถั่วฤดูใบไม้ผลิต่อไปนี้:
getBean ()-> สร้างอินสแตนซ์-> AutoWired-> set property-> afterpropertiesset
นั่นคือการเรียกใช้วิธี getObject เรียกว่าเร็วกว่าวิธี Afterpropertiesset
จากนั้นมาปรับเปลี่ยน MyFactoryBean ให้เป็นดังนี้:
วัตถุสาธารณะ getObject () โยนข้อยกเว้น {// todo วิธีการที่สร้างอัตโนมัติ stubif (null == ทดสอบ) {afterpropertiesset ();} การทดสอบการส่งคืน;} โมฆะสาธารณะ AfterPropertIesset () พ่นข้อยกเว้น {ถ้า (null == ทดสอบ) {system.out.println ("ชื่อ:" + this.name); ทดสอบ = การทดสอบใหม่ (); test.name = adpendentBean.dosomething () + this.name;}} นั่นคือถ้าคุณตัดสินภายใน getObject ก่อนให้ test==null จากนั้นเรียก AfterPropertIesset แล้วถ้า test==null กำลังสร้างอินสแตนซ์ทดสอบภายใน AfterPropertIesset มันดูดีและฉันต้องการแก้ปัญหาของเราจริงๆ แต่ในความเป็นจริงมันยังไม่ได้ผลเพราะ AfterPropertIesset ใช้ BeferentBean ภายในและในเวลานี้ depentBean=null
3.3 กำลังคิดเกี่ยวกับวิธีการแก้ปัญหา
3.2 การวิเคราะห์เหตุผลคือ MyFactoryBean ถูกสร้างขึ้นครั้งแรกและ DepentBean ถูกสร้างขึ้นในระหว่างกระบวนการสร้าง MyFactoryBean เมื่อสร้าง depentBean จำเป็นต้องมีอินสแตนซ์ของ MyFactoryBean ที่ไม่ได้ใช้งาน จากนั้นวิธีการ getObject จะถูกเรียกก่อนโทรหลัง propropertiesset ดังนั้น null จะถูกส่งคืน
ดังนั้นหากคุณสร้าง DepentBean ก่อนแล้วสร้าง MyFactoryBean? ทำการวิเคราะห์ต่อไปนี้:
ก่อนอื่น DePentBean จะถูกสร้างอินสแตนซ์และเพิ่มลงใน SingletonFactories
อินสแตนซ์ depentBean จะทำการทดสอบโดยอัตโนมัติดังนั้นอินสแตนซ์ทดสอบจะถูกสร้างขึ้นก่อน
สร้างอินสแตนซ์ทดสอบแล้วเข้าร่วม SingletonFactories
อินสแตนซ์การทดสอบจะฉีดอินสแตนซ์ depentBean ที่แอตทริบิวต์ดังนั้นมันจะ getBean(“depentBean”);
getBean(“depentBean”) พบว่ามีการพึ่งพาใน SingletonFactories อยู่แล้วและส่งคืนวัตถุขึ้นอยู่กับ Bean
เนื่องจาก InderentBean ไม่ใช่ถั่วจากโรงงาน
อินสแตนซ์ทดสอบจะถูกฉีดลงในอินสแตนซ์ depentBean สำเร็จการทดสอบการเริ่มต้นอินสแตนซ์ของอินสแตนซ์ตกลง
อินสแตนซ์ของ DepentBean AutoWired Instance OK
จากการวิเคราะห์นี้มันเป็นไปได้ที่จะสร้าง depentbean ก่อนจากนั้นสร้างอินสแตนซ์ myFactoryBean แก้ไข XML เป็นสิ่งต่อไปนี้:
<bean id = "ขึ้นอยู่กับ"> </epean> <Bean ID = "Test"> <property name = "AdpendentBean"> <ref bean = "ขึ้นอยู่กับ"/> </คุณสมบัติ> <property name = "name" value = "zlx"> </property> </epean>
ทดสอบผลการทดสอบ:
ชื่อ: zlx
สวัสดี: zlx
หากเป็นเรื่องปกติแล้วตามการวิเคราะห์นี้หากมีการปรับการกำหนดค่า XML ข้างต้นมันจะทำให้เกิดข้อผิดพลาดอย่างแน่นอนเนื่องจากการทดสอบถูกสร้างขึ้นเร็วกว่าการพึ่งพาอาศัยกันและนี่เป็นจริงหลังจากการทดสอบ นอกจากนี้ยังสามารถจินตนาการได้ว่าเมื่อถั่วโรงงานอาศัยถั่วโรงงานมันจะล้มเหลวอย่างหลีกเลี่ยงไม่ได้โดยไม่คำนึงถึงคำสั่งของการประกาศ
3.3 ความคิด
ก่อนหน้านี้จะฉีดยาขึ้นพึ่งพานั้นจะต้องใช้ใน MyFactoryBean จากนั้นฉีด MyFactoryBean และปัญหาได้รับการแก้ไข ดังนั้นหากคุณต้องการใช้วัตถุที่สร้างขึ้นด้วย id = "ทดสอบ" ในถั่วอื่นถั่วนี้ควรฉีดอย่างไร?
มันจะประสบความสำเร็จในทำนองเดียวกันหรือไม่? ทิ้งไว้ให้ทุกคนคิด ^^
ชั้นเรียนสาธารณะ usetest {@autowiredPrivate ทดสอบการทดสอบ;}<bean id = "usetest"> </epean> <bean id = "ขึ้นอยู่กับ"> </epean> <bean id = "test"> <property name = "ขึ้นอยู่กับ"> <ref bean = "ขึ้นอยู่กับ"
4. สรุป
เมื่อถั่วธรรมดาพึ่งพาซึ่งกันและกันคำสั่งของการฉีดถั่วจะไม่เกี่ยวข้อง แต่เมื่อถั่วจากโรงงานและถั่วธรรมดาพึ่งพาซึ่งกันและกันถั่วธรรมดาจะต้องได้รับการยกตัวอย่างก่อน นี่เป็นเพราะความพิเศษของถั่วจากโรงงานนั่นคือมันมีวิธี getObject
โอเคข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่าเนื้อหาของบทความนี้จะมีค่าอ้างอิงบางอย่างสำหรับการศึกษาหรือที่ทำงานของทุกคน หากคุณมีคำถามใด ๆ คุณสามารถฝากข้อความไว้เพื่อสื่อสาร ขอบคุณสำหรับการสนับสนุน Wulin.com