Как Spring решает эту проблему: bean-компонент A зависит от bean-компонента B, а bean-компонент B - от bean-компонента A.
Как Spring решает эту проблему: bean-компонент A зависит от bean-компонента B, а bean-компонент B - от bean-компонента A.
Ответы:
Как говорили другие ответы, Spring просто заботится об этом, создавая bean-компоненты и вводя их по мере необходимости.
Одним из последствий является то, что инъекция bean-компонента / установка свойств могут происходить в порядке, отличном от того, что, по-видимому, подразумевают ваши XML-файлы проводки. Поэтому вам нужно быть осторожным, чтобы ваши установщики свойств не выполняли инициализацию, основанную на уже вызванных других установщиках. Способ справиться с этим - объявить bean-компоненты как реализующие InitializingBean
интерфейс. Это требует от вас реализации afterPropertiesSet()
метода, и именно здесь вы выполняете критическую инициализацию. (Я также включаю код, чтобы проверить, действительно ли установлены важные свойства.)
Справочное руководство Spring объясняет , как круговые зависимости разрешаются. Сначала создаются экземпляры бобов, а затем вводятся друг в друга.
Рассмотрим этот класс:
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
еще не полностью инициализирован.
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
В кодовой базе, с которой я работаю (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 участвуют в циклическом графе зависимостей. Мы решили это, реализовав ApplicationContextAware и InitializingBean и вручную внедрив bean-компоненты.
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 (false), чтобы предотвратить многие проблемы в будущем.
Проблема ->
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: Ошибка при создании bean-компонента с именем 'A': Запрошенный bean-компонент в настоящее время находится в создании: есть ли неразрешимая циклическая ссылка?
Решение 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 };
}
Он просто делает это. Он создает a
иb
и вставляет каждый в другой (используя свои методы установки).
В чем проблема?
Из ссылки Spring :
Как правило, вы можете доверять Spring в правильных поступках. Он обнаруживает проблемы конфигурации, такие как ссылки на несуществующие bean-компоненты и циклические зависимости, во время загрузки контейнера. Spring устанавливает свойства и разрешает зависимости как можно позже, когда компонент действительно создается.
Контейнер Spring может разрешать циклические зависимости на основе Setter, но выдает исключение времени выполнения BeanCurrentlyInCreationException в случае циклических зависимостей на основе конструктора. В случае циклической зависимости на основе Setter контейнер IOC обрабатывает ее иначе, чем в типичном сценарии, в котором он полностью настраивал бы взаимодействующий bean-компонент перед его внедрением. Например, если Bean A зависит от Bean B, а Bean B - от Bean C, контейнер полностью инициализирует C, прежде чем вводить его в B, и как только B полностью инициализирован, он вводится в A. Но в случае циклической зависимости один одного боба передается другому до его полной инициализации.
Скажем, A зависит от B, тогда Spring сначала создаст экземпляр A, затем B, затем установит свойства для B, а затем установит B в A.
Но что, если B также зависит от A?
Насколько я понимаю: Spring только что обнаружил, что A был построен (конструктор выполнен), но не полностью инициализирован (не все инъекции выполнены), ну, он подумал, что все в порядке, это терпимо, что A не полностью инициализирован, просто установите это not- на данный момент полностью инициализированы экземпляры A в B. После того, как B полностью инициализирован, он был установлен в A, и, наконец, A был полностью инициализирован.
Другими словами, он просто заранее выставляет A на B.
Для зависимостей через конструктор Sprint просто выбрасывает BeanCurrentlyInCreationException, чтобы разрешить это исключение, установите для lazy-init значение true для bean-компонента, который зависит от других через конструктор-arg.
Если вы обычно используете инъекцию конструктора и не хотите переключаться на инъекцию свойств, тогда инъекция метода поиска Spring позволит одному bean-компоненту лениво искать другой и, следовательно, обходить циклическую зависимость. См. Здесь: http://docs.spring.io/spring/docs/1.2.9/reference/beans.html#d0e1161
Внедрение конструктора завершается ошибкой, если между Spring beans существует круговая зависимость. Таким образом, в этом случае внедрение Setter помогает решить проблему.
По сути, внедрение конструктора полезно для обязательных зависимостей, для необязательных зависимостей лучше использовать внедрение через сеттер, потому что мы можем выполнять повторное внедрение.
Если два bean-компонента зависят друг от друга, мы не должны использовать инъекцию конструктора в обоих определениях bean-компонента. Вместо этого мы должны использовать внедрение сеттера в любой из bean-компонентов. (конечно, мы можем использовать инъекцию установщика в обоих определениях bean, но инъекции конструктора в обоих выбрасывают 'BeanCurrentlyInCreationException'
См Спринг документ в « https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#resources-resource »