Jak rozwiązuje to wiosna: fasola A jest zależna od fasoli B, a fasola B od fasoli A.
Jak rozwiązuje to wiosna: fasola A jest zależna od fasoli B, a fasola B od fasoli A.
Odpowiedzi:
Jak powiedziały inne odpowiedzi, Spring po prostu się tym zajmuje, tworząc ziarna i wstrzykując je w razie potrzeby.
Jedną z konsekwencji jest to, że wstrzykiwanie / ustawianie właściwości fasoli może zachodzić w innej kolejności, niż sugerowałyby to pliki łączników XML. Musisz więc uważać, aby ustawienia ustawiające właściwości nie przeprowadzały inicjalizacji, która polega na wywołaniu innych metod ustawiających. Sposobem na rozwiązanie tego problemu jest zadeklarowanie fasoli jako implementującej InitializingBean
interfejs. Wymaga to zaimplementowania afterPropertiesSet()
metody i tutaj wykonujesz krytyczną inicjalizację. (Dołączam również kod, aby sprawdzić, czy ważne właściwości zostały faktycznie ustawione).
Reference Manual Wiosna wyjaśnia, w jaki sposób wzajemnie od siebie zależnych są rozwiązane. Ziarna są najpierw tworzone, a następnie wstrzykiwane do siebie nawzajem.
Rozważ tę klasę:
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;
}
}
I podobna klasa 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;
}
}
Jeśli wtedy miałeś ten plik konfiguracyjny:
<bean id="a" class="mypackage.A">
<property name="b" ref="b" />
</bean>
<bean id="b" class="mypackage.B">
<property name="a" ref="a" />
</bean>
Podczas tworzenia kontekstu przy użyciu tej konfiguracji zobaczysz następujące dane wyjściowe:
Creating instance of A
Creating instance of B
Setting property a of B instance
Setting property b of A instance
Zauważ, że kiedy a
jest wstrzykiwany b
, a
nie jest jeszcze w pełni zainicjowany.
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
W bazie kodu, z którą pracuję (1 milion + wierszy kodu) mieliśmy problem z długimi czasami uruchamiania, około 60 sekund. Byliśmy coraz 12000+ FactoryBeanNotInitializedException .
To, co zrobiłem, to ustawienie warunkowego punktu przerwania w 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;
}
gdzie to jest destroySingleton(beanName)
, wydrukowałem wyjątek z kodem warunkowego punktu przerwania:
System.out.println(ex);
return false;
Najwyraźniej dzieje się tak, gdy FactoryBean są zaangażowane w cykliczny wykres zależności. Rozwiązaliśmy to, wdrażając ApplicationContextAware i InitializingBean oraz ręcznie wprowadzając ziarna.
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();
}
}
Skróciło to czas uruchamiania do około 15 sekund.
Dlatego nie zawsze zakładaj, że wiosna może być dobra w rozwiązywaniu tych odniesień.
Z tego powodu polecam wyłączenie cyklicznego rozwiązywania zależności za pomocą AbstractRefreshableApplicationContext # setAllowCircularReferences (false), aby zapobiec wielu przyszłym problemom.
Problem ->
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 };
}
// Spowodowane przez: org.springframework.beans.factory.BeanCurrentlyInCreationException: Błąd podczas tworzenia komponentu bean o nazwie „A”: Żądany komponent bean jest obecnie tworzony: czy istnieje nierozwiązywalne cykliczne odwołanie?
Rozwiązanie 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
}
Rozwiązanie 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 };
}
Po prostu to robi. Tworzy instancje a
i b
i wstrzykuje je do siebie (używając ich metod ustawiających).
Jaki jest problem?
Ze źródła wiosennego :
Generalnie możesz ufać Springowi, że postąpi właściwie. Wykrywa problemy z konfiguracją, takie jak odwołania do nieistniejących komponentów bean i zależności cyklicznych, w czasie ładowania kontenera. Spring ustawia właściwości i rozwiązuje zależności tak późno, jak to możliwe, kiedy fasola jest faktycznie tworzona.
Kontener Spring jest w stanie rozwiązać zależności cykliczne oparte na narzędziu Setter, ale daje wyjątek czasu wykonywania BeanCurrentlyInCreationException w przypadku zależności cyklicznych opartych na konstruktorze. W przypadku zależności cyklicznej opartej na programie Setter, kontener IOC obsługuje go inaczej niż w typowym scenariuszu, w którym w pełni skonfigurowałby współpracujący komponent bean przed wstrzyknięciem. Na przykład, jeśli Bean A ma zależność od Bean B i Bean B od Bean C, kontener w pełni inicjuje C przed wstrzyknięciem go do B i po pełnym zainicjowaniu B jest wstrzykiwany do A. Ale w przypadku zależności cyklicznej, jeden ziaren jest wstrzykiwana do drugiej, zanim zostanie w pełni zainicjowana.
Powiedzmy, że A zależy od B, a następnie Spring najpierw utworzy wystąpienie A, potem B, a następnie ustawi właściwości dla B, a następnie ustawi B na A.
Ale co, jeśli B również zależy od A?
Rozumiem: Spring właśnie odkrył, że A został skonstruowany (wykonano konstruktor), ale nie został w pełni zainicjowany (nie wykonano wszystkich wstrzyknięć), cóż, pomyślał, jest OK, to tolerowane, że A nie jest w pełni zainicjowany, po prostu ustaw to nie- na razie w pełni zainicjalizowane instancje A w B. Po pełnym zainicjowaniu B został ustawiony na A, a ostatecznie A został teraz w pełni zainicjowany.
Innymi słowy, po prostu wystawia z wyprzedzeniem A na B.
W przypadku zależności za pośrednictwem konstruktora Sprint po prostu wyrzuć BeanCurrentlyInCreationException, aby rozwiązać ten wyjątek, ustaw lazy-init na true dla komponentu bean, który zależy od innych, poprzez sposób konstruktora-arg.
Jest to jasno wyjaśnione tutaj . Podziękowania dla Eugena Paraschiva.
Zależność cykliczna jest zapachem projektu, napraw ją lub użyj @Lazy dla zależności, która powoduje problem w jej obejściu.
Jeśli generalnie używasz iniekcji konstruktora i nie chcesz przełączać się na wstrzykiwanie właściwości, metoda wyszukiwania Springa pozwoli jednej fasoli leniwie wyszukać drugą, a tym samym obejść cykliczną zależność. Zobacz tutaj: http://docs.spring.io/spring/docs/1.2.9/reference/beans.html#d0e1161
Wstrzyknięcie konstruktora kończy się niepowodzeniem, gdy między fasolkami wiosennymi występuje zależność kołowa. Więc w tym przypadku my Setter wtrysk pomaga rozwiązać problem.
Zasadniczo iniekcja konstruktora jest przydatna w przypadku zależności obowiązkowych, w przypadku zależności opcjonalnych lepiej jest używać iniekcji settera, ponieważ możemy wykonać ponowne wstrzyknięcie.
Jeśli dwie fasole są od siebie zależne, nie powinniśmy używać wtrysku konstruktora w obu definicjach fasoli. Zamiast tego musimy użyć zastrzyku ustawiającego w dowolnej fasoli. (oczywiście możemy użyć iniekcji setter w obu definicjach bean, ale iniekcje konstruktora w obu rzutach 'BeanCurrentlyInCreationException'
Zapoznaj się z dokumentem Spring pod adresem „ https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#resources-resource ”