वसंत में परिपत्र निर्भरता


100

वसंत इसे कैसे हल करता है: बीन ए बीन बी पर निर्भर है, और बीन बी सेम ए पर निर्भर है।


2
अच्छा लेख baeldung.com/circular-d dependencies
in-

एक और उपयोगी लेख जो यह बताता है कि परिपत्र निर्भरताएँ कैसे होती हैं: octoperf.com/blog/2018/02/15/spring-circular-d dependencies
जेरोम एल

जवाबों:


42

जैसा कि अन्य जवाबों में कहा गया है, स्प्रिंग बस इसका ध्यान रखती है, फलियों को बनाना और उन्हें आवश्यकतानुसार इंजेक्ट करना।

परिणामों में से एक यह है कि बीन इंजेक्शन / प्रॉपर्टी सेटिंग एक अलग क्रम में हो सकती है कि आपकी XML वायरिंग फ़ाइलों का क्या मतलब होगा। इसलिए आपको सावधान रहने की जरूरत है कि आपकी संपत्ति बसने वाले शुरुआती काम पर निर्भर नहीं करती है जो पहले से ही कहे जा रहे अन्य बाशिंदों पर निर्भर है। इससे निपटने का तरीका InitializingBeanइंटरफ़ेस को लागू करने के रूप में बीन्स की घोषणा करना है। इसके लिए आपको afterPropertiesSet()विधि को लागू करने की आवश्यकता है , और यह वह जगह है जहाँ आप महत्वपूर्ण आरंभीकरण करते हैं। (मैं यह जांचने के लिए कोड भी शामिल करता हूं कि महत्वपूर्ण गुण वास्तव में सेट किए गए हैं।)


76

वसंत संदर्भ मैनुअल बताते हैं कि कैसे परिपत्र निर्भरता हल कर रहे हैं। फलियों को पहले से सूंघा जाता है, फिर एक दूसरे में इंजेक्ट किया जाता है।

इस वर्ग पर विचार करें:

package mypackage;

public class A {

    public A() {
        System.out.println("Creating instance of A");
    }

    private B b;

    public void setB(B b) {
        System.out.println("Setting property b of A instance");
        this.b = b;
    }

}

और एक समान श्रेणी B:

package mypackage;

public class B {

    public B() {
        System.out.println("Creating instance of B");
    }

    private A a;

    public void setA(A a) {
        System.out.println("Setting property a of B instance");
        this.a = a;
    }

}

यदि आपके पास यह कॉन्फ़िगरेशन फ़ाइल थी:

<bean id="a" class="mypackage.A">
    <property name="b" ref="b" />
</bean>

<bean id="b" class="mypackage.B">
    <property name="a" ref="a" />
</bean>

इस कॉन्फ़िगरेशन का उपयोग करते हुए संदर्भ बनाते समय आपको निम्न आउटपुट दिखाई देंगे:

Creating instance of A
Creating instance of B
Setting property a of B instance
Setting property b of A instance

ध्यान दें कि कब aइंजेक्ट किया जाता है b, aअभी तक पूरी तरह से आरंभिक नहीं है।


26
यही कारण है कि स्प्रिंग को बिना किसी तर्क के एक निर्माता की आवश्यकता है ;-)
क्रिस थॉम्पसन

15
नहीं अगर आप अपनी सेम परिभाषाओं में कंस्ट्रक्टर तर्कों का उपयोग करते हैं! (लेकिन उस मामले में आपके पास एक परिपत्र निर्भरता नहीं हो सकती है।)
रिचर्ड फर्न

1
@ रिचर्ड समाधान प्रदान करने के बजाय समस्या के बारे में आपकी पोस्ट क्या स्पष्टीकरण देती है?
gstackoverflow

4
यदि आप कंस्ट्रक्टर इंजेक्शन का उपयोग करने का प्रयास करते हैं, तो त्रुटि संदेश हैorg.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
X. Wo Satuk

19

कोडबेस में मैं (1 मिलियन + लाइनों की कोड) के साथ काम कर रहा हूं, हमें लंबे स्टार्टअप समय, लगभग 60 सेकंड के साथ समस्या थी। हमें 12000+ FactoryBeanNotInitializedException मिल रही थी ।

मैंने जो किया था वह AbstractBeanFactory # doGetBean में एक सशर्त विराम बिंदु निर्धारित किया गया था

catch (BeansException ex) {
   // Explicitly remove instance from singleton cache: It might have been put there
   // eagerly by the creation process, to allow for circular reference resolution.
   // Also remove any beans that received a temporary reference to the bean.
   destroySingleton(beanName);
   throw ex;
}

यह कहाँ है destroySingleton(beanName) मैंने सशर्त ब्रेकपॉइंट कोड के साथ अपवाद मुद्रित किया है:

   System.out.println(ex);
   return false;

स्पष्ट रूप से ऐसा तब होता है जब FactoryBean s चक्रीय निर्भरता ग्राफ में शामिल होता है । हमने ApplicationContextAware और InitializingBean को लागू करके और सेम को मैन्युअल रूप से इंजेक्ट करके इसे हल किया ।

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class A implements ApplicationContextAware, InitializingBean{

    private B cyclicDepenency;
    private ApplicationContext ctx;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        ctx = applicationContext;
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        cyclicDepenency = ctx.getBean(B.class);
    }

    public void useCyclicDependency()
    {
        cyclicDepenency.doSomething();
    }
}

इसने स्टार्टअप समय को लगभग 15 सेकंड तक घटा दिया।

इसलिए हमेशा यह न समझें कि वसंत आपके लिए इन संदर्भों को हल करने में अच्छा हो सकता है।

इस कारण से मैं कई भविष्य की समस्याओं को रोकने के लिए AbstractRefreshableApplicationContext # setAllowCircularReferences (झूठा) के साथ चक्रीय निर्भरता संकल्प को अक्षम करने की सिफारिश करूंगा


3
दिलचस्प सिफारिश। मेरी काउंटर सिफारिश केवल यह करने के लिए होगी कि यदि आपको संदेह है कि परिपत्र संदर्भ एक प्रदर्शन समस्या पैदा कर रहे हैं। (यह एक ऐसी चीज़ को तोड़ने के लिए शर्म की बात होगी जिसे किसी समस्या को ठीक करने की कोशिश करने की ज़रूरत नहीं है, जिसे ठीक करने की ज़रूरत नहीं है।)
स्टीफन सी

2
यह सर्कुलर डिपेंडेंसी की अनुमति देने के लिए मेन्टेनेंस हेल्प के लिए एक फिसलन भरी ढलान है, सर्कुलर डिपेंडेंसी से आपके आर्किटेक्चर को दोबारा डिजाइन करना वास्तव में मुश्किल हो सकता है, क्योंकि यह हमारे मामले में था। मोटे तौर पर हमारे लिए इसका मतलब यह था कि हमें स्टार्टअप के दौरान दो बार डेटाबेस कनेक्शन मिले, क्योंकि सर्कुलर निर्भरता में सेशनफैक्ट्री शामिल थी। अन्य स्थितियों में बीन 12,000+ बार की तात्कालिक होने के कारण बहुत अधिक विनाशकारी चीजें हो सकती थीं। सुनिश्चित करें कि आपको अपनी सेम लिखना चाहिए ताकि वे उन्हें नष्ट करने का समर्थन करें लेकिन पहली बार में इस व्यवहार की अनुमति क्यों दें?
jontejj

@jontejj, आप एक कुकी के लायक हैं
सीरपाइम

14

समस्या ->

Class A {
    private final B b; // must initialize in ctor/instance block
    public A(B b) { this.b = b };
}


Class B {
    private final A a; // must initialize in ctor/instance block
    public B(A a) { this.a = a };
 }

// इसके कारण: org.springframework.beans.factory.BeanCurrentlyInCreationException: नाम 'ए' के ​​साथ बीन बनाने में त्रुटि: अनुरोधित बीन वर्तमान में निर्माण में है: क्या एक अनपेक्षित परिपत्र संदर्भ है?

समाधान 1 ->

Class A {
    private B b; 
    public A( ) {  };
    //getter-setter for B b
}

Class B {
    private A a;
    public B( ) {  };
    //getter-setter for A a
}

समाधान 2 ->

Class A {
    private final B b; // must initialize in ctor/instance block
    public A(@Lazy B b) { this.b = b };
}

Class B {
    private final A a; // must initialize in ctor/instance block
    public B(A a) { this.a = a };
}

12

यह बस करता है। यह त्वरित aऔरb , और प्रत्येक को एक दूसरे में इंजेक्ट करता है (उनके सेटर विधियों का उपयोग करके)।

समस्या क्या है?


9
@javaguy: नहीं, यह नहीं होगा।
skaffman

@skaffman के बाद ही संपत्तियों की विधि के उपयोग के बाद उचित तरीका है?
gstackoverflow

6

से वसंत संदर्भ :

आप आम तौर पर सही काम करने के लिए वसंत पर भरोसा कर सकते हैं। यह कंटेनर लोड-टाइम पर गैर-मौजूद बीन्स और परिपत्र निर्भरता के संदर्भ जैसे कॉन्फ़िगरेशन समस्याओं का पता लगाता है। वसंत गुण निर्धारित करता है और निर्भरता को यथासंभव देर से हल करता है, जब बीन वास्तव में बनाई जाती है।


6

स्प्रिंग कंटेनर सेटर-आधारित परिपत्र निर्भरता को हल करने में सक्षम है, लेकिन कंस्ट्रक्टर-आधारित परिपत्र निर्भरता के मामले में एक रनटाइम अपवाद BeanCurrentlyInCreationException देता है। सेटर-आधारित परिपत्र निर्भरता के मामले में, आईओसी कंटेनर इसे एक विशिष्ट परिदृश्य से अलग तरीके से संभालता है, जिसमें यह इंजेक्शन लगाने से पहले सहयोगी सेम को पूरी तरह से कॉन्फ़िगर करेगा। उदाहरण के लिए, यदि बीन ए पर बीन बी और बीन बी पर निर्भरता है, तो कंटेनर बी को इंजेक्ट करने से पहले पूरी तरह से सी को इनिशियलाइज़ करता है और एक बार बी को पूरी तरह से इनिशियलाइज़ करने के बाद इसे ए पर इंजेक्ट कर दिया जाता है, लेकिन परिपत्र निर्भरता के मामले में, एक पूरी तरह से शुरू होने से पहले फलियों को दूसरे में इंजेक्ट किया जाता है।


5

कहते हैं कि A, B पर निर्भर करता है, तो वसंत पहले A, फिर B से बी लेगा, फिर B के लिए गुण सेट करेगा, फिर B को A में सेट करेगा।

लेकिन क्या होगा अगर बी भी ए पर निर्भर करता है?

मेरी समझ यह है: वसंत ने पाया कि ए का निर्माण किया गया है (कंस्ट्रक्टर निष्पादित), लेकिन पूरी तरह से आरंभीकृत नहीं (नहीं किए गए सभी इंजेक्शन), ठीक है, यह सोचा, यह ठीक है, यह सहन करने योग्य है कि ए पूरी तरह से आरंभीकृत नहीं है, बस इसे सेट करें- अभी के लिए बी में पूरी तरह से प्रारंभिक ए उदाहरण। बी पूरी तरह से शुरू होने के बाद, इसे ए में सेट किया गया था, और आखिरकार, ए को अब पूरी तरह से शुरू किया गया था।

दूसरे शब्दों में, यह पहले से ए से बी को उजागर करता है।

कंस्ट्रक्टर के माध्यम से निर्भरता के लिए, स्प्रिंट सिर्फ बीनक्रिकेटलीइन्क्रिएशन एक्ससेप्शन को फेंक देते हैं, इस अपवाद को हल करने के लिए, बीन के लिए आलसी-इनिट सेट करें जो कंस्ट्रक्टर-आरजी मार्ग के माध्यम से दूसरों पर निर्भर करता है।


सरल और सबसे अच्छी व्याख्या में से एक।
श्रीतम जगदेव २०

5

इसकी स्पष्ट व्याख्या यहां दी गई है । युगेन पारशिव को धन्यवाद।

परिपत्र निर्भरता एक डिजाइन गंध है, या तो इसे ठीक करें या निर्भरता के लिए @Lazy का उपयोग करें जो इसे हल करने के लिए समस्या का कारण बनता है।


3

यदि आप आम तौर पर कंस्ट्रक्टर-इंजेक्शन का उपयोग करते हैं और संपत्ति-इंजेक्शन पर स्विच नहीं करना चाहते हैं, तो स्प्रिंग का लुकअप-मेथड- इनजेक्शन एक बीन को आलसी रूप से दूसरे को देखने देगा और इसलिए चक्रीय निर्भरता को हल कर देगा। यहां देखें: http://docs.spring.io/spring/docs/1.2.9/reference/beans.html#d0e1161


3

वसंत फलियों के बीच सर्कुलर डिपेंडेंसी होने पर कंस्ट्रक्टर इंजेक्शन फेल हो जाता है। तो इस मामले में हम सेटर इंजेक्शन समस्या को हल करने में मदद करते हैं।

मूल रूप से, कंस्ट्रक्टर इंजेक्शन अनिवार्य निर्भरता के लिए, सेटर इंजेक्शन का उपयोग करने के लिए बेहतर है, क्योंकि हम फिर से इंजेक्शन कर सकते हैं।


0

यदि दो फलियाँ एक दूसरे पर निर्भर हैं तो हमें बीन की दोनों परिभाषाओं में कंस्ट्रक्टर इंजेक्शन का उपयोग नहीं करना चाहिए। इसके बजाय हमें सेम में से किसी एक में सेटर इंजेक्शन का उपयोग करना होगा। (बेशक हम दोनों सेम परिभाषाओं में सेटर इंजेक्शन एन का उपयोग कर सकते हैं, लेकिन दोनों 'बीनक्रिकेटलीइन्क्रिएशन एक्ससेप्शन' में कंस्ट्रक्टर इंजेक्शन फेंक देते हैं।

वसंत डॉक को " https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#resource-resource " पर देखें

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.