Spring은 콩 사이의 원형 의존성을위한 고유 한 솔루션을 가지고 있습니다. 핵심 사항은 레벨 3 캐시입니다. 물론,이 솔루션은 모든 문제를 해결할 수 없으며 Bean Singleton 모드에서 비 구조물의 원형 의존성 만 해결할 수 있습니다.
우리는 a-> b-> ca의 초기화 순서에서 시작합니다. 즉, b의 인스턴스가 a의 콩에 필요하다는 것을 의미합니다. c의 인스턴스는 b의 콩에 필요하며, c의 인스턴스는 C의 콩에 필요합니다. 전제 조건을 사용할 수있게되면 시작할 수 있습니다. 우리가 처음으로 초기화 할 것이라는 데는 의심의 여지가 없습니다. 초기화 방법은 org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean 입니다
보호 된 <T> T DOGETBEAN (최종 문자열 이름, 최종 클래스 <T> 필수 유형, 최종 개체 [] args, 부울 typeCheckonly) beansexception {final String beanname = transformedBeanName (이름); 물체 콩; // 수동으로 등록 된 싱글 톤에 대한 싱글 톤 캐시를 간절히 확인하십시오. Object SharedInstance = GetSingleton (Beanname); // 초점 1 if (sharedInstance! = null && args == null) {if (logger.isdebugenabled ()) {if (issingletoncurrentyIncreation (beanname)) {logger.debug ( "싱글 톤 콩의 싱글 톤 콩의 인스턴스 '' + beanname +" '); } else {logger.debug ( "싱글 톤 Bean의 캐시 된 인스턴스를 반환" " + beanname +" ""); }} bean = getObjectForBeanInstance (sharedInstance, name, beanname, null); } else {// 이미이 Bean 인스턴스를 만들고 있다면 실패합니다. // 우리는 원형 참조 내에서 가정됩니다. if (isprotoTypeCurrentyIncreation (beanname)) {새 beancurrentyIncreationException (beanname); } //이 공장에 Bean 정의가 있는지 확인하십시오. BeanFactory ParentBeanFactory = getParentBeanFactory (); if (parentbeanfactory! = null &&! containsbeandefinition (beanname)) {// 찾을 수 없음 -> 부모를 확인하십시오. String nametolookup = OriginalBeanName (이름); if (args! = null) {// 명백한 args가있는 부모에게 위임됩니다. return (t) parentbeanfactory.getBean (nametolookup, args); } else {// args 없음 -> 표준 getbean 메소드를 대표합니다. ParentBeanFactory.getBean (nametolookup, requesstype)을 반환합니다. }} if (! typecheckonly) {MarkBeanAscreated (beanname); } try {Final RootBeanDefinition MBD = GetMergedLocalBeanDefinition (BeanName); CheckmergedBeendefinition (MBD, Beanname, Args); // 현재 콩이 의존하는 콩의 초기화를 보장합니다. 문자열 [] fextson = mbd.getDependson (); if (sextentson! = null) {for (string eppendsonbean : fextson) {if (iSdependent (beanname, fextsonbean)) {throw new beancreationException (mbd.getResourcedescription (), BeanName, "" " + beanname +"와 ' + depentsonbean + "'사이의 관계에 따라 관계가 있습니다. } registerDependentBean (부양애, BeanName); getbean (부양비); }} // bean 인스턴스를 만듭니다. if (mbd.issingleton ()) {// conforme 2 sharedInstance = getSingleton (beanname, new objectortory <object> () {@override public object getObject ()는 beansexception {return retrybean (beanname, mbd, args);} catch (beansexception ex) {// singleton chache : // 원형 참조 해상도를 허용합니다. bean = getObjectForBeanInstance (sharedInstance, name, beanname, mbd); } else if (mbd.isprototype ()) {// 프로토 타입 -> 새 인스턴스를 만듭니다. Object PrototypeInstance = null; {prefrotoTypeCreceation (beanname)을 시도하십시오. Prototypeinstance = CreateBean (Beanname, MBD, Args); } 마침내 {후 프로토 타입이 생성 (BeanName); } bean = getObjectForBeanInstance (PrototypeInstance, name, beanname, mbd); } else {String scopename = mbd.getScope (); 최종 범위 범위 = this.scopes.get (scopename); if (scope == null) {throw new new ElegalStateException ( "스코프 이름에 등록되지 않은 스코프 없음 '" + ScoPename + "'"); } try {object scopedInstance = scope.get (beanname, new ObjectFactory <botort> () {@override public object (getObject ()는 beansexception {prectoTypeCreation (beanname); retud createbean (beanname, mbd, args); {afterPrototyPecrcrcr (beanname);}); bean = getObjectForBeanInstance (scopedinstance, name, beanname, mbd); } catch (불법 스테이트 exception ex) {Throw New BeancreationException (Beaname, "Scope '" + Scopename + "' '는 현재 스레드에 대해 활성화되지 않으며,이 Bean에 대한 스코프 프록시 정의를 고려하십시오. }}} catch (beansexception ex) {cleanupfterbeancreationfailure (beanname); ex 던지기; }} // 필요한 유형이 실제 Bean 인스턴스 유형과 일치하는지 확인합니다. if (requirettype! = null && bean! = null &&! requirettype.isAssignableFrom (bean.getClass ())) {try {return getTypeConverter (). ancoundifnecessary (bean, requesstype); } catch (typEmismatchException ex) {if (logger.isdebugenabled ()) {logger.debug ( "bean '" + name + "' '' '' ' + name +"'가 필요한 유형으로 [ " + classUtils.getqualifiedName (requiLifiedName (requiLifiedName)]", ex); } 새 beannotofrequiredtypeexception 던지기 (name, requesstype, bean.getClass ()); }} return (t) bean; } 이 방법은 매우 길다. 조금씩 이야기합시다. 우리의 초점을 먼저 보자. Object sharedInstance = getSingleton(beanName )은 이름을 기반으로 싱글 톤 컬렉션에서 싱글 톤 객체를 얻습니다. 이 방법을 살펴보고 마지막으로 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean) 입니다.
보호 된 개체 GetSingleton (String Beanname, Boolean allowearlyReference) {Object SingletonObject = this.singletonObjects.get (BeanName); if (SingletonObject == null && issingletoncurrentyIncreation (beanname)) {synchronized (this.singletonObjects) {SingletonObject = this.earlySingletonObjects.get (beanname); if (singletonObject == null && allowEarlyReference) {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 에는 인스턴스화 된 싱글토 노브 jects가 배치됩니다. 두 번째 레벨 earlySingletonObjects 미리 노출 된 싱글 톤 객체를 저장합니다 (완전히 조립되지 않음). 세 번째 수준의 SingletonFactories는 인스턴스화 할 객체의 객체 공장을 저장합니다. 레벨 3 캐시를 설명한 후 논리를 살펴 보겠습니다. 내가 this.singletonObjects.get(beanName) null을 반환합니다. 그런 다음 isSingletonCurrentlyInCreation 2 차 캐시에서 데이터를 얻을 수 있는지 여부를 결정합니다.
public boolean issingletoncurrentyincreation (String beanname) {return this.singletonscurrentyincreation.contains (beanname); } Beanname이 singletonsCurrentlyInCreation 세트에 포함 된 Beanname이 포함되어 있습니까? 이전에 설정할 장소는 없으므로 확실히 포함되지 않습니다. 따라서이 메소드는 False를 반환하면 후속 프로세스가 남지 않습니다. getSingleton 메소드는 NULL을 반환합니다.
Focus 2를 살펴 보겠습니다. 또한 getSingleton 이지만 콩을 만드는 실제 과정입니다. 우리는 익명의 대상 객체가 전달된다는 것을 알 수 있습니다. Bean을 만드는 실제 방법 인 CreateBean이라는 getObject 메소드. 물론 우리는 그것을 제쳐두고 getSingleton 방법을 계속 볼 수 있습니다.
public object getsingleton (String beanname, ObjectFactory <?> singletonfactory) {assert.notnull (Beanname, " 'beanname'은 null이되어서는 안됩니다"); 동기화 된 (this.singletonObjects) {Object SingletonObject = this.singletonObjects.get (beanname); if (singletonobject == null) {if (this.singletonscurrenty -Indestruction) {awnamenamenotallowedexception (a beanname, "싱글 톤 콩 생성이 허용되지 않는다. } if (logger.isdebugenabled ()) {logger.debug ( "싱글 톤 Bean의 공유 인스턴스 생성" " + beanname +" ""); } beforesingletoncreation (beanname); 부울 뉴저지 = 거짓; 부울 recordsuppressedexceptions = (this.suppressedExceptions == null); if (RecordSuppressedExceptions) {this.suppressedExceptions = new LinkedHashset <Exception> (); } try {SingletonObject = SingletOnfactory.getObject (); Newsingleton = True; } catch (delegalStateException ex) {// 싱글 톤 객체가 그 동안 -> // exc SingletonObject = this.singletonobjects.get (beanname); if (SingletonObject == NULL) {Throw Ex; }} catch (beancreationException ex) {if (RecordsUppressedExceptions) {for (예외 억제 : this.suppressedExceptions) {ex.addrelatedCause (suppressedException); }} thr ex; } 마침내 {if (RecordSuppressedExceptions) {this.suppressedExceptions = null; } AfterSingletonCreation (BeanName); } if (Newsingleton) {addsingleton (Beanname, SingletonObject); }} return (SingletonObject! = null_object? singletonobject : null); }} 이 메소드의 첫 번째 문장 Object singletonObject = this.singletonObjects.get(beanName) 첫 번째 수준 캐시에서 데이터를 가져옵니다. 그런 다음 beforeSingletonCreation 방법이 호출됩니다.
보호 된 void beforeSingletOncreation (String beanname) {if (! this.increationcheckexClusions.contains (beanname) &&! this.singletonscurrentyIncreation.add (beanname)) {throw new beancurrenty -sincreationException (beanname); }} 그중에는 Beanname을 singletonsCurrentlyInCreation 세트에 추가하는 과정이 있습니다. 이 세트는 매우 중요하며 나중에 사용됩니다. 그런 다음 getObject SingletOnfactory의 GetObjec 방법을 호출하여 실제 생성 과정을 수행하십시오. 위에서 언급 한 실제 창조 과정을 살펴 보겠습니다 createBean 그 안의 핵심 논리는 doCreateBean 입니다.
보호 된 개체 docreatebean (Final String Beanname, Final RootBeendefinition MBD, Final Object [] args) {// 콩을 인스턴스화합니다. beanwrapper instanceWrapper = null; if (mbd.issingleton ()) {instanceWrapper = this.FactoryBeanInstanceCache.remove (beanname); } if (instanceWrapper == null) {instanceWrapper = createBeanInstance (BeanName, mbd, args); } final Object bean = (instanceWrapper! = null? instanceWrapper.getWrapedInstance () : null); class <?> beantype = (instanceWrapper! = null? instanceWrapper.getWrapedClass () : null); // 후 처리기가 병합 된 Bean 정의를 수정하도록 허용합니다. Synchronized (MBD.PostProcessingLock) {if (! mbd.postprocessed = true; }} // 싱글 레틀 톤은 원형 참조를 해결할 수 있도록 싱글 톤을 열렬히 캐시합니다. // 걱정 3 부울 초기 징글 론 XPOSURE = (mbd.issingleton () && this.lowercircularReferences && issingletoncurrentyIncreation (beanname)); if (earlysingletonexposure) {if (logger.isdebugenabled ()) {logger.debug ( "잠재적 인 원형 참조를 해결하기 위해" + beanname + " '' + beanname +" '); } addSingletOnfactory (beanname, new ObjectFactory <bood> () {@override public object getObject ()가 beansexception {return getearlyBeanReference (beanname, mbd, bean);}}); } // bean 인스턴스를 초기화합니다. 물체 ExposedObject = Bean; try {populebean (beanname, mbd, instanceWrapper); if (exposedObject! = null) {ExposedObject = 초기화 바 (BeanName, ExposedObject, MBD); }} catch (trashable ex) {if (ex instanceof beancreationException && beanname.equals (((beancreationException))) .getBeanName ())) {strow (beancreationException) ex; } else {Throw New BeanCreationException (mbd.getResourcedEscription (), BeanName, "Bean 실패 초기화", ex); }} if (earlysingletonexposure) {개체 earlysingletonreference = getsingleton (beanname, false); if (earlysingletonReference! = null) {if (ExposedObject == bean) {ExposedObject = EarlySingleTonReference; } else if (! this. this.hallowRawinjectionDespiteWrapping && hasdependentBean (beanname)) {string [] fexendentBeans = getDependentBeans (beanname); <string> realdependentbeans = new LinkedHashset <string> (fexendbeans.length); for (string feleventBean : fexendentBeans) {if (! removeNifeTeRfeAdforteCheckonly (fectionentBean)) {realDependentBeans.Add (fectionentBean); }} if (! 실제 의존적 비안 (! realdependentBeans.isempty)) {새 beancurrentyIncreationException (beanname, "eAnname '," + beanname + "'는 다른 콩에 주입되었습니다. 콩은 " +"콩의 최종 버전을 사용하지 않습니다. 이것은 종종 과도한 유형 일치의 결과입니다. 예를 들어 '허용'플래그와 함께 " +" 'getBeannamesofType'를 고려하십시오. }}}}}} // Bean을 일회용으로 등록하십시오. {registerDisposableBeanifNecessary (BeanName, Bean, MBD)를 시도하십시오. } catch (beanDefinitionValidationException ex) {Throw New BeanCreationException (mbd.getResourcedEscription (), "Invalid Destruction Signature", ex); } return ExposedObject; } createBeanInstance 반사를 사용하여 객체를 만듭니다. 심사 포인트 3 earlySingletonExposure 속성 값을 살펴 보겠습니다. 판단 지점 중 하나 isSingletonCurrentlyInCreation(beanName)
public boolean issingletoncurrentyincreation (String beanname) {return this.singletonscurrentyincreation.contains (beanname); } SingletonscurrentyIncreation 세트가 사용된다는 것을 알았습니다. Beanname은 위의 단계로 채워 졌으므로 찾을 수 있습니다. 따라서, earlySingletonExposure 속성은 다른 조건과 함께 사실로 결정되며, 다음 과정은 addSingletonFactory 추가된다. 다음은 Beanname (a)에 해당하는 객체 공장입니다. getObject 방법의 구현은 getEarlyBeanReference 방법을 통해 달성됩니다. 먼저 AddSingletonFactory의 구현을 살펴 보겠습니다.
보호 된 void AddSingleTonFactory (String Beanname, ObjectFactory <?> singletonfactory) {assert.notnull (SingletonFactory, "Singleton Factory는 무효가되어서는 안됩니다"); 동기화 (this.singletonObjects) {if (! this.singletonObjects.containskey (beanname)) {this.singletonfactory.put (beanname, singletonfactory); this.earlysingletonobjects.remove (beanname); this.registeredsingletons.add (beanname); }}} 3 단계 캐시 SingletOnfactories에 데이터를 저장하여 BeanName을 기반으로 두 번째 레벨 캐시 데이터를 지 웁니다. 여기에는 매우 중요한 점이 있으며, 이는 값을 3 단계 캐시로 설정하는 것입니다. 이는 Spring의 원형 종속성 처리의 핵심 지점입니다. getEarlyBeanReference 방법은 getObject의 구현입니다. A로 채워진 객체 인스턴스를 반환하는 것으로 간단히 고려 될 수 있습니다. 레벨 3 캐시를 설정 한 후 A 객체의 속성을 채우는 프로세스가 시작됩니다. 다음 설명에는 소스 코드 프롬프트가 없으며 간단한 소개 만 있습니다.
A를 채울 때 B 형 Bean이 필요하다는 것을 알았으므로 GetBean 방법을 계속 호출하여 만들었습니다. 메모리 프로세스는 위와 정확히 동일합니다. 그런 다음 C 형 Bean을 채우는 과정으로 갔고 GetBean (C)도 요청됩니다. 재산 A를 채울 때 Getbean (a)에 전화했습니다. 여기서부터 나는 동일한 코드 인 Object sharedInstance = getSingleton(beanName), 이라고 불렀지 만 처리 로직은 완전히 다릅니다.
보호 된 개체 GetSingleton (String Beanname, Boolean allowearlyReference) {Object SingletonObject = this.singletonObjects.get (BeanName); if (SingletonObject == null && issingletoncurrentyIncreation (beanname)) {synchronized (this.singletonObjects) {SingletonObject = this.earlySingletonObjects.get (beanname); if (singletonObject == null && allowEarlyReference) {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); } 그럼에도 불구하고, 물체는 싱글토 노 젝트에서 얻을 수 없습니다. A는 singletonsCurrentlyInCreation 세트에 있기 때문에 다음 논리에 들어가서 earlySingletonObjects 초기의 Cache에서 가져 왔지만 여전히 발견되지 않았습니다. 그런 다음 그는 3 단계 캐시 singletonFactories getObject 메소드를 호출하여 완전히 채워지지 않은 A의 인스턴스 개체를 얻은 다음 세 번째 레벨 캐시 데이터를 삭제하고 두 번째 레벨 캐시 데이터를 채우고이 객체 A를 반환합니다. C는 A의 인스턴스 채우기에 따라 다르지만 A는 불완전합니다. C 스타일 충전물이 완료되는 방법에 관계없이 C를 1 단계 캐시 singletonObjects 에 넣고 동시에 2 단계 및 3 단계 캐시의 데이터를 정리할 수 있습니다. 동일한 과정에서 C가 채워지면 B가 채워지면 B가 채워집니다. 마찬가지로, B가 채워지면 B가 채워지면 A가 채워집니다. 이것이 스프링이 원형 참조를 해결하는 방법입니다.
위는이 기사의 모든 내용입니다. 모든 사람의 학습에 도움이되기를 바랍니다. 모든 사람이 wulin.com을 더 지원하기를 바랍니다.