ฤดูใบไม้ผลิมีวิธีแก้ปัญหาของตัวเองสำหรับการพึ่งพาวงกลมระหว่างถั่ว จุดสำคัญคือแคชระดับสาม แน่นอนว่าการแก้ปัญหานี้ไม่สามารถแก้ปัญหาทั้งหมดได้ แต่สามารถแก้ปัญหาการพึ่งพาแบบวงกลมของผู้ที่ไม่ใช่โครงสร้างในโหมดถั่วซิงเกิลได้
เราจะเริ่มต้นจากลำดับการเริ่มต้นของ a-> b-> ca ซึ่งหมายความว่าจำเป็นต้องมีอินสแตนซ์ของ B ในถั่วของ A อินสแตนซ์ของ C ในถั่วของ B จำเป็นต้องมีอินสแตนซ์ของ A ในถั่วของ C เมื่อข้อกำหนดเบื้องต้นพร้อมใช้งานเราสามารถเริ่มต้นได้ ไม่ต้องสงสัยเลยว่าเราจะเริ่มต้นก่อน วิธีการเริ่มต้นคือ org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
ได้รับการป้องกัน <t> t dogetBean (ชื่อสตริงสุดท้าย, คลาสสุดท้าย <t> ต้องการ tType, วัตถุสุดท้าย [] args, boolean typeCheckonly) พ่น beansexception {สตริงสุดท้าย beanname = transformedBeanName (ชื่อ); ถั่ววัตถุ; // ตรวจสอบแคช Singleton อย่างกระตือรือร้นสำหรับซิงเกิลที่ลงทะเบียนด้วยตนเอง Object SharedInstance = GetSingleton (ชื่อ Beanname); // โฟกัส 1 ถ้า (SharedInstance! = null && args == null) {ถ้า (logger.isdebugenabled ()) {ถ้า (issingletoncurrentlyincreation (Beanname)) {logger.debug ( } else {logger.debug ("การส่งคืนอินสแตนซ์แคชของ Singleton Bean '" + Beanname + "'"); }} bean = getObjectForBeanInstance (SharedInstance, ชื่อ, BeanName, Null); } else {// ล้มเหลวหากเรากำลังสร้างอินสแตนซ์ถั่วนี้แล้ว: // เราสันนิษฐานว่าอยู่ในการอ้างอิงแบบวงกลม if (isprototypecurrentlyIncreation (BeanName)) {โยน beancurrentlyIncreationException ใหม่ (BeanName); } // ตรวจสอบว่าคำจำกัดความของถั่วอยู่ในโรงงานนี้หรือไม่ Beanfactory ParentBeanFactory = getParentBeanFactory (); if (parentBeanFactory! = null &&! containsBeanDefinition (BeanName)) {// ไม่พบ -> ตรวจสอบผู้ปกครอง สตริง nametolookup = OriginalBeanName (ชื่อ); if (args! = null) {// การมอบหมายให้ผู้ปกครองด้วย args ที่ชัดเจน return (t) ParentBeanFactory.getBean (Nametolookup, Args); } else {// no args -> มอบหมายให้วิธี GetBean มาตรฐาน ส่งคืน ParentBeanFactory.getBean (Nametolookup, ต้องการประเภท); }} if (! typecheckonly) {markbeanascreated (Beanname); } ลอง {final rootBeanDefinition mbd = getMergedLocalBeanDefinition (BeanName); CheckMergedBeanDefinition (MBD, Beanname, Args); // รับประกันการเริ่มต้นของถั่วที่ถั่วปัจจุบันขึ้นอยู่กับ String [] ขึ้นอยู่กับ = mbd.getDependson (); if (ผู้อยู่อาศัย! = null) {สำหรับ (String ispersonBean: ขึ้นอยู่กับ) {ถ้า (isdependent (Beanname, edcelsonbean)) {โยน beancreationexception ใหม่ (mbd.getResourcedescription (), beanname, "ขึ้นอยู่กับความสัมพันธ์ระหว่าง" } registerDependentBean (ผู้อยู่ใต้บังคับบัญชา, Beanname); GetBean (ผู้อยู่ใต้บังคับบัญชา); }} // สร้างอินสแตนซ์ถั่ว if (mbd.issingleton ()) {// ข้อกังวล 2 SharedInstance = getSingleton (beanname, ObjectFactory ใหม่ <Object> () {@Override วัตถุสาธารณะ getObject () โยน beansexception {ลอง {return createBean (beanname, mbd, args); วางไว้ที่นั่น // อย่างกระตือรือร้นโดยกระบวนการสร้างเพื่อให้ความละเอียดอ้างอิงแบบวงกลม Bean = getObjectForBeanInstance (SharedInstance, ชื่อ, Beanname, MBD); } อื่นถ้า (mbd.isprototype ()) {// มันเป็นต้นแบบ -> สร้างอินสแตนซ์ใหม่ Object Prototypeinstance = null; ลอง {beforePrototypecReation (Beanname); Prototypeinstance = CreateBean (Beanname, MBD, Args); } ในที่สุด {afterprototypecreation (Beanname); } Bean = getObjectForBeanInstance (Prototypeinstance, ชื่อ, Beanname, MBD); } else {string scopename = mbd.getScope (); ขอบเขตขอบเขตสุดท้าย = this.scopes.get (scopename); if (scope == null) {โยนใหม่ unlilelStateException ("ไม่มีขอบเขตที่ลงทะเบียนสำหรับชื่อขอบเขต '" + scopename + "'"); } ลอง {Object scopedInstance = scope.get (beanname, ObjectFactory ใหม่ <jobch> () {@Override วัตถุสาธารณะ getObject () พ่น beansexception {beforeprotypecreation (beanname); ลอง {return createBean (beanname, mbd, args); - Bean = getObjectForBeanInstance (scopedInstance, ชื่อ, Beanname, MBD); } catch (unglegalstateException ex) {โยน beancreationexception ใหม่ (Beanname, "ขอบเขต" " + scopename +" 'ไม่ได้ใช้งานสำหรับกระทู้ปัจจุบันพิจารณา " +" การกำหนดพร็อกซีที่มีขอบเขตสำหรับถั่วนี้หากคุณตั้งใจจะอ้างถึงจากซิงเกิล " }}} catch (beansexception ex) {cleanupafterbeancreationfailure (Beanname); โยนอดีต; }} // ตรวจสอบว่าประเภทที่ต้องการตรงกับประเภทของอินสแตนซ์ถั่วจริงหรือไม่ if (จำเป็นต้องใช้! = null && bean! = null &&! EledentyType.isassignableFrom (bean.getClass ())) {ลอง {return getTypeconverter (). convertIfneSICE (ถั่ว } catch (typemismatchexception ex) {ถ้า (logger.isdebugenabled ()) {logger.debug ("ล้มเหลวในการแปลงถั่ว '" + ชื่อ + "' เป็นประเภทที่ต้องการ [" + classutils.getqualifiedName } โยน beannotofrequiredTypeException ใหม่ (ชื่อ, NeveyType, Bean.getClass ()); }} return (t) ถั่ว; - วิธีนี้ยาวมากเรามาพูดถึงเรื่องนี้ทีละเล็กทีละน้อย มาดูโฟกัสของเราก่อน Object sharedInstance = getSingleton(beanName ) ได้รับวัตถุซิงเกิลจากคอลเลกชันของ Singletons ตามชื่อ ลองดูที่วิธีนี้และในที่สุดมันก็เป็น org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)
วัตถุที่ได้รับการป้องกัน GetSingleton (String Beanname, Boolean อนุญาตให้ใช้งานได้) {Object SingletonObject = this.singletonobjects.get (Beanname); if (singletonObject == null && issingletonCurrentlyIncreation (beanName)) {ซิงโครไนซ์ (this.singletoNobjects) {singletonObject = this.earlysingletonobjects.get (beanname); if (singletonObject == null && lewlowlyReference) {ObjectFactory <?> singletonFactory = 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); - ทุกคนต้องใส่ใจกับวิธีนี้มันสำคัญมาก เราพูดถึงแคชระดับ 3 ที่จุดเริ่มต้นและหนึ่งในจุดใช้งานอยู่ที่นี่ แคชระดับ 3 คือแคชไหน? singletonObjects ของแคชระดับแรกถูกวางไว้ด้วย SingletonObjects แบบอินสแตนซ์ ระดับที่สอง earlySingletonObjects เก็บวัตถุซิงเกิลตันที่เปิดเผยล่วงหน้า (ไม่ได้ประกอบอย่างเต็มที่) SingletonFactories ระดับที่สามเก็บโรงงานวัตถุของวัตถุที่จะสร้างอินสแตนซ์ หลังจากอธิบายแคชระดับ 3 ลองมาดูตรรกะ ครั้งแรกที่ฉันเข้ามาใน this.singletonObjects.get(beanName) ส่งคืน NULL จากนั้น isSingletonCurrentlyInCreation จะกำหนดว่าข้อมูลสามารถรับได้ในแคชทุติยภูมิหรือไม่
บูลีนสาธารณะ issingletonCurrentlyIncreation (String beanname) {return this.singletonsurrentlyincreation.contains (Beanname); - Beanname จะรวมอยู่ในชุด singletonsCurrentlyInCreation มีชื่อถั่วที่เข้ามาหรือไม่? ไม่มีสถานที่ที่จะตั้งค่ามาก่อนดังนั้นจึงไม่ได้รวมไว้อย่างแน่นอน ดังนั้นวิธีนี้จะส่งคืนเท็จและกระบวนการที่ตามมาจะไม่ถูกทิ้งไว้ วิธี getSingleton ส่งคืนค่า NULL
ลองดูที่โฟกัส 2 นอกจากนี้ยังเป็น getSingleton แต่มันเป็นกระบวนการที่แท้จริงในการสร้างถั่ว เราจะเห็นได้ว่าวัตถุวัตถุที่ไม่ระบุชื่อจะถูกส่งผ่านวิธี getObject ที่เรียกว่า createBean วิธีการจริงในการสร้างถั่ว แน่นอนว่าเราสามารถแยกมันออกไปและดูวิธี getSingleton เราต่อไป
วัตถุสาธารณะ GetSingleton (String Beanname, ObjectFactory <?> SingletonFactory) {assert.notnull (Beanname, "'Beanname' ต้องไม่เป็น Null"); ซิงโครไนซ์ (this.singletonobjects) {Object SingletonObject = this.singletonobjects.get (Beanname); if (singletonObject == null) {ถ้า (this.singletonscurrentyindestruction) {โยน beancreationnotlowedexception (Beanname, "การสร้างถั่วซิงเกิลไม่ได้รับอนุญาตในขณะที่โรงงานแห่งนี้ถูกทำลาย" + " } if (logger.isdebugenabled ()) {logger.debug ("การสร้างอินสแตนซ์ที่ใช้ร่วมกันของ Singleton Bean '" + Beanname + "'"); } BEFORESINGLETONCREATION (BeanName); บูลีน newsingleton = false; Boolean RecordsuppressedExceptions = (this.suppressedExceptions == null); if (recordsuppressedExceptions) {this.suppressedExceptions = new LinkedHashSet <Exception> (); } ลอง {singletonObject = singletonfactory.getObject (); Newsingleton = true; } catch (unglegalStateException ex) {// มีวัตถุซิงเกิลตันปรากฏขึ้นโดยปริยายในระหว่างนี้ -> // ถ้าใช่ให้ดำเนินการต่อไปเนื่องจากข้อยกเว้นระบุสถานะนั้น singletonObject = this.singletonobjects.get (Beanname); if (singletonObject == null) {โยน ex; }} catch (beancreationexception ex) {ถ้า (recordsuppressedexceptions) {สำหรับ (ข้อยกเว้นระงับการรับรู้: this.suppressedExceptions) {ex.addrelatedCause (SuppressedException); }} โยน ex; } ในที่สุด {ถ้า (recordsuppressedExceptions) {this.suppressedExceptions = null; } AftersingletonCreation (BeanName); } if (newsingleton) {addsingleton (beanname, singletonobject); }} return (singletonObject! = null_object? singletonobject: null); - ประโยคแรกของวิธีการนี้ Object singletonObject = this.singletonObjects.get(beanName) ดึงข้อมูลจากแคชระดับแรกซึ่งเป็นโมฆะแน่นอน วิธีการ beforeSingletonCreation จะถูกเรียก
เป็นโมฆะที่ได้รับการป้องกัน beforesingletoncreation (สตริง Beanname) {ถ้า (! this.increationcheckexclusions.contains (Beanname) &&! this.singletschurrentyincreation.add (Beanname)) - ในหมู่พวกเขาคือกระบวนการของการเพิ่ม Beanname ลงในชุด singletonsCurrentlyInCreation ชุดนี้มีความสำคัญมากและจะใช้ในภายหลัง จากนั้นเรียกใช้วิธี getObject ของ SingletonFactory เพื่อดำเนินการกระบวนการสร้างจริง มาดูกระบวนการสร้างจริงที่กล่าวถึงข้าง createBean ตรรกะหลักข้างในนั้นคือ doCreateBean
วัตถุที่ได้รับการป้องกัน docreateBean (สตริงสุดท้าย Beanname, RootBeanDefinition สุดท้าย, วัตถุสุดท้าย [] args) {// อินสแตนซ์ถั่ว BeanWrapper InstantWrapper = null; if (mbd.issingleton ()) {instanceWrapper = this.factoryBeanInstancecache.remove (BeanName); } if (instanceWrapper == null) {instanceWrapper = createBeanInstance (BeanName, MBD, ARGS); } วัตถุสุดท้าย Bean = (InstantWrapper! = null? InstantWrapper.getWrappingInstance (): null); คลาส <s?> beanTepe = (อินสแตนซ์ wrapper! = null? instantwrapper.getWrappedclass (): null); // อนุญาตให้โพสต์โปรเซสเซอร์ปรับเปลี่ยนคำจำกัดความถั่วที่ผสาน ซิงโครไนซ์ (mbd.postprocessinglock) {ถ้า (! mbd.postprocessed) {applymergedebeandefinitionpostprocessors (MBD, Beantype, Beanname); mbd.postprocessed = true; }} // Cache Singletons อย่างกระตือรือร้นเพื่อให้สามารถแก้ไขการอ้างอิงแบบวงกลม // แม้เมื่อถูกเรียกโดยอินเทอร์เฟซวงจรชีวิตเช่น BeanFactoryAware // ข้อกังวล 3 บูลีน EarlySingleTonexPosure = (mbd.issingleton () && this.allowcircularreferences && issingletoncurrentyincreation (Beanname)); if (ournsingletonexposure) {ถ้า (logger.isdebugenabled ()) {logger.debug ("ถั่วแคชอย่างกระตือรือร้น '" + Beanname + "' เพื่ออนุญาตให้แก้ไขการอ้างอิงแบบวงกลมที่อาจเกิดขึ้น"); } AddsingLetOnFactory (BeanName, New ObjectFactory <Object> () {@Override วัตถุสาธารณะ getObject () พ่น beansexception {return getearlyBeanReference (Beanname, MBD, Bean);}}); } // เริ่มต้นอินสแตนซ์ถั่ว Object ExposedObject = ถั่ว; ลอง {PopulateBean (Beanname, MBD, InstanceWrapper); if (encosedObject! = null) {encosedObject = initializeBean (BeanName, ExposedObject, MBD); }} catch (throwable ex) {if (ex instanceof beancreationexception && beanname.equals (((BeancreationException) Ex) .getBeanName ())) {โยน (BeancreationException) อดีต; } else {โยน beancreationexception ใหม่ (mbd.getResourcedescription (), Beanname, "การเริ่มต้นของถั่วล้มเหลว", ex); }} if (ournsingletonexposure) {Object EarlySingLetOnReference = GetSingleton (BeanName, False); if (ournsingletonReference! = null) {ถ้า (encosedObject == Bean) {ensosedObject = ournesingletOnreference; } อื่นถ้า (! this.allowrawinjectedDespitewrapping && hasdependentBean (beanname)) {String [] ขึ้นอยู่กับ Beans = getDependentBeans (BeanName); ตั้งค่า <String> realDependentBeans = new LinkedHashSet <String> (ขึ้นอยู่กับความยาว); สำหรับ (String adperentbean: ขึ้นอยู่กับ) {if (! removesingletonifcreatedfortypecheckonly (ขึ้นอยู่กับ)) {realdependentBeans.add (ขึ้นอยู่กับ); }} if (! realDedEdentBeans.isEmpty ()) {โยน beancurrentlyIncreationException (Beanname, "ถั่วที่มีชื่อ '" + beanname + "' ถูกฉีดเข้าไปในถั่วอื่น ๆ ที่กล่าวว่าถั่วอื่น ๆ ไม่ได้ใช้ถั่ว " +" รุ่นสุดท้าย }}}}} // ลงทะเบียนถั่วเป็นทิ้ง ลอง {registerDisposableBeanifnitary (Beanname, Bean, MBD); } catch (beandefinitionValidationException ex) {โยน beancreationException ใหม่ (mbd.getResourcedescription (), Beanname, "ลายเซ็นการทำลายล้างที่ไม่ถูกต้อง", Ex); } return encosedObject; - createBeanInstance สร้างวัตถุโดยใช้การสะท้อนกลับ ลองดูที่จุดตัดสิน 3 earlySingletonExposure มูลค่าแอตทริบิวต์ หนึ่งในคะแนนการตัดสิน isSingletonCurrentlyInCreation(beanName)
บูลีนสาธารณะ issingletonCurrentlyIncreation (String beanname) {return this.singletonsurrentlyincreation.contains (Beanname); - ฉันพบว่ามีการใช้ชุด SingletonscurrentlyIncreation Beanname ได้รับการกรอกในขั้นตอนข้างต้นดังนั้นจึงสามารถพบได้ ดังนั้นคุณสมบัติของ earlySingletonExposure จึงถูกกำหนดให้เป็นจริงร่วมกับเงื่อนไขอื่น ๆ และกระบวนการต่อไปนี้จะถูกเพิ่ม addSingletonFactory นี่คือโรงงานวัตถุที่สอดคล้องกับชื่อถั่ว (a) การใช้วิธี getObject นั้นทำได้ผ่านวิธี getEarlyBeanReference ก่อนอื่นมาดูการใช้งานของ AddsingletonFactory
Void Protected AddsingletonFactory (String Beanname, ObjectFactory <?> SingletonFactory) {assert.notnull (SingletonFactory, "Singleton Factory ต้องไม่เป็นโมฆะ"); ซิงโครไนซ์ (this.singletonobjects) {ถ้า (! this.singletonobjects.containskey (Beanname)) {this.singletonfactory.put (Beanname, SingletonFactory); this.earlysingletonobjects.remove (Beanname); this.registeredsingletons.add (Beanname); - การจัดเก็บข้อมูลลงในแคชระดับที่สาม SingletonFactories ล้างข้อมูลแคชระดับที่สองตามชื่อ Beanname มีจุดสำคัญมากที่นี่ซึ่งคือการกำหนดค่าเป็นแคชระดับที่สามซึ่งเป็นจุดหลักของการประมวลผลการพึ่งพาวงกลมของฤดูใบไม้ผลิ วิธี getEarlyBeanReference คือการใช้งาน GetObject มันสามารถพิจารณาได้ว่ามันส่งคืนอินสแตนซ์ของวัตถุที่เต็มไปด้วย A หลังจากตั้งค่าแคชระดับ 3 กระบวนการของการกรอกคุณสมบัติของวัตถุเริ่มต้น คำอธิบายต่อไปนี้ไม่มีข้อความแจ้งซอร์สโค้ดเพียงแค่แนะนำสั้น ๆ
เมื่อเติม A ฉันพบว่าจำเป็นต้องใช้ถั่วชนิด B ดังนั้นฉันจึงเรียกวิธีการ GetBean เพื่อสร้างมันต่อไป กระบวนการหน่วยความจำนั้นเหมือนกับข้างต้น จากนั้นฉันก็ไปที่กระบวนการเติมถั่ว Type C และการเรียก GetBean (C) เดียวกันจะถูกเรียกให้ดำเนินการ เมื่อเติมทรัพย์สิน A ฉันเรียก GetBean (a) มาต่อจากที่นี่ฉันเรียก Object sharedInstance = getSingleton(beanName), รหัสเดียวกัน แต่ตรรกะการประมวลผลนั้นแตกต่างกันอย่างสิ้นเชิง
วัตถุที่ได้รับการป้องกัน GetSingleton (String Beanname, Boolean อนุญาตให้ใช้งานได้) {Object SingletonObject = this.singletonobjects.get (Beanname); if (singletonObject == null && issingletonCurrentlyIncreation (beanName)) {ซิงโครไนซ์ (this.singletoNobjects) {singletonObject = this.earlysingletonobjects.get (beanname); if (singletonObject == null && lewlowlyReference) {ObjectFactory <?> singletonFactory = 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); - ยังไม่สามารถรับวัตถุได้จาก singletonobjects เนื่องจาก A อยู่ในชุด singletonsCurrentlyInCreation เขาจึงเข้าสู่ตรรกะต่อไปนี้และนำมาจากแคชระดับที่สอง earlySingletonObjects แต่ก็ยังไม่พบ จากนั้นเขาก็พบวัตถุที่เกี่ยวข้องจากแคชระดับที่สาม singletonFactories โรงงานเรียกใช้วิธี getObject เพื่อรับวัตถุอินสแตนซ์ของ A ที่ไม่ได้รับการเติมเต็มอย่างสมบูรณ์จากนั้นลบข้อมูลแคชระดับที่สามเติมข้อมูลแคชระดับที่สองและส่งคืนวัตถุนี้ A. C ขึ้นอยู่กับการเติมอินสแตนซ์ของ A แม้ว่านี่จะไม่สมบูรณ์ ไม่ว่าการเติม C-style จะเสร็จสมบูรณ์อย่างไรคุณสามารถใส่ C ในแคช singletonObjects ระดับแรกและทำความสะอาดข้อมูลของแคชระดับที่สองและระดับที่สามในเวลาเดียวกัน ในกระบวนการเดียวกันหาก C ขึ้นอยู่กับ B ถูกเติมเต็ม B จะเต็มไปด้วย B ในทำนองเดียวกันถ้า B ขึ้นอยู่กับ A ถูกเติมเต็ม นี่คือวิธีที่ฤดูใบไม้ผลิแก้ปัญหาการอ้างอิงแบบวงกลม
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่ามันจะเป็นประโยชน์ต่อการเรียนรู้ของทุกคนและฉันหวังว่าทุกคนจะสนับสนุน wulin.com มากขึ้น