Bahar bunu nasıl çözer: A fasulyesi B fasulyesine ve B fasulyesi A fasulyesine bağımlıdır.
Bahar bunu nasıl çözer: A fasulyesi B fasulyesine ve B fasulyesi A fasulyesine bağımlıdır.
Yanıtlar:
Diğer cevapların da söylediği gibi, Spring sadece ilgilenir, fasulyeleri oluşturur ve gerektiği gibi enjekte eder.
Sonuçlardan biri, fasulye enjeksiyonu / özellik ayarının XML kablolama dosyalarınızın ima ettiğinden farklı bir sırada gerçekleşebilmesidir. Bu nedenle, mülk belirleyicilerinizin zaten çağrılmış olan diğer ayarlayıcılara dayanan başlatma yapmamasına dikkat etmeniz gerekir. Bununla başa çıkmanın yolu, fasulyeleri InitializingBean
arayüzü uyguluyor olarak ilan etmektir . Bu, afterPropertiesSet()
yöntemi uygulamanızı gerektirir ve kritik başlatmayı burada yaptığınız yer burasıdır. (Ayrıca önemli özelliklerin gerçekten ayarlandığını kontrol etmek için kod ekliyorum.)
Bahar referans kılavuzu dairesel bağımlılıkları çözülür açıklar. Çekirdekler önce örneklenir, ardından birbirlerine enjekte edilir.
Bu sınıfı düşünün:
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;
}
}
Ve benzer bir sınıf 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;
}
}
Daha sonra bu yapılandırma dosyasına sahipseniz:
<bean id="a" class="mypackage.A">
<property name="b" ref="b" />
</bean>
<bean id="b" class="mypackage.B">
<property name="a" ref="a" />
</bean>
Bu yapılandırmayı kullanarak bir bağlam oluştururken aşağıdaki çıktıyı görürsünüz:
Creating instance of A
Creating instance of B
Setting property a of B instance
Setting property b of A instance
Ne zaman geldiğini hatırlatırız a
enjekte edilir b
, a
henüz tam olarak başlatılmadı.
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
Üzerinde çalıştığım kod tabanında (1 milyondan fazla kod satırı) 60 saniye gibi uzun başlatma süreleriyle ilgili bir sorun yaşadık. 12000+ FactoryBeanNotInitializedException alıyorduk .
Yaptığım şey, AbstractBeanFactory # doGetBean'da koşullu bir kesme noktası belirlemekti
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;
}
burada destroySingleton(beanName)
istisnayı koşullu kesme noktası koduyla yazdırdım:
System.out.println(ex);
return false;
Görünüşe göre bu, FactoryBean ler döngüsel bir bağımlılık grafiğine dahil . ApplicationContextAware ve InitializingBean uygulayarak ve fasulyeleri manuel olarak enjekte ederek sorunu çözdük .
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();
}
}
Bu, başlangıç süresini yaklaşık 15 saniyeye düşürdü.
Bu nedenle, her zaman baharın bu referansları sizin için çözmede iyi olabileceğini düşünmeyin.
Bu nedenle, gelecekteki birçok sorunu önlemek için AbstractRefreshableApplicationContext # setAllowCircularReferences (false) ile döngüsel bağımlılık çözümlemesini devre dışı bırakmanızı öneririm .
Sorun ->
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 };
}
// Sebep: org.springframework.beans.factory.BeanCurrentlyInCreationException: 'A' adında fasulye oluşturulurken hata: İstenen fasulye şu anda oluşturuluyor: Çözümlenemeyen bir döngüsel başvuru var mı?
1.Çözüm ->
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.Çözüm ->
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 };
}
Sadece yapıyor. Somutlaştırır a
veb
, ve (kendilerine ait ayarlayıcı yöntemleri kullanılarak) diğer içine her biri enjekte eder.
Sorun ne?
Gönderen Bahar Referans :
Doğru şeyi yapması için genellikle Spring'e güvenebilirsiniz. Var olmayan çekirdeklere ve döngüsel bağımlılıklara referanslar gibi yapılandırma sorunlarını konteyner yükleme zamanında algılar. Fasulye gerçekten oluşturulduğunda, Spring özellikleri ayarlar ve bağımlılıkları olabildiğince geç çözer.
Spring kapsayıcı, Setter tabanlı döngüsel bağımlılıkları çözebilir, ancak Oluşturucu tabanlı döngüsel bağımlılıklar durumunda BeanCurrentlyInCreationException bir çalışma zamanı istisnası verir. Setter tabanlı döngüsel bağımlılık durumunda, IOC konteyneri, onu enjekte etmeden önce işbirliği yapan çekirdeği tamamen yapılandıracağı tipik bir senaryodan farklı şekilde ele alır. Örneğin, Bean A, Bean B'ye ve Bean B'ye Bean C'ye bağımlıysa, konteyner C'yi B'ye enjekte etmeden önce tamamen başlatır ve B tamamen başlatıldığında A'ya enjekte edilir. Ancak döngüsel bağımlılık durumunda, bir çekirdeklerden% 100'ü diğerine tam olarak başlatılmadan önce enjekte edilir.
A'nın B'ye bağlı olduğunu varsayalım, o zaman Bahar önce A'yı, sonra B'yi, sonra B için özellikleri ayarlayacak, sonra B'yi A'ya ayarlayacaktır.
Peki ya B de A'ya bağlıysa?
Anladığım kadarıyla: Spring, A'nın inşa edildiğini (kurucu yürüttüğünü), ancak tam olarak başlatılmadığını (tüm enjeksiyonlar yapılmadı) buldu, iyi, sorun değil, A'nın tam olarak başlatılmaması tolere edilebilir, sadece şunu ayarla- Şimdilik tamamen başlatılmış A örnekleri B'de. B tamamen başlatıldıktan sonra, A'ya ayarlandı ve son olarak, A şimdi tamamen başlatıldı.
Başka bir deyişle, önceden A'yı B'ye maruz bırakır.
Yapıcı aracılığıyla bağımlılıklar için, Sprint sadece BeanCurrentlyInCreationException'ı atar, bu istisnayı çözmek için, yapıcı-arg yolu aracılığıyla başkalarına bağımlı olan çekirdek için lazy-init'i true olarak ayarlayın.
Genelde yapıcı enjeksiyonu kullanıyorsanız ve özellik enjeksiyonuna geçmek istemiyorsanız, Spring'in arama yöntemi enjeksiyonu, bir çekirdeğin diğerini tembel bir şekilde aramasına ve dolayısıyla döngüsel bağımlılığı geçici olarak çözmesine izin verecektir. Buraya bakın: http://docs.spring.io/spring/docs/1.2.9/reference/beans.html#d0e1161
Bahar çekirdekleri arasında Dairesel Bağımlılık olduğunda Yapıcı Enjeksiyonu başarısız olur. Bu durumda biz Setter enjeksiyonu sorunu çözmeye yardımcı oluyoruz.
Temel olarak, Constructor Injection, zorunlu bağımlılıklar için kullanışlıdır, isteğe bağlı bağımlılıklar için Setter enjeksiyonunu kullanmak daha iyidir çünkü yeniden enjeksiyon yapabiliriz.
İki fasulye birbirine bağımlıysa, her iki fasulye tanımında da Constructor enjeksiyonunu kullanmamalıyız. Bunun yerine, herhangi bir fasulyede setter enjeksiyonu kullanmalıyız. (elbette bean tanımlarının her ikisinde de setter injection kullanabiliriz, ancak her ikisinde de yapıcı enjeksiyonları 'BeanCurrentlyInCreationException' atar.
" Https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#resources-resource " adresindeki Bahar belgesine bakın.