Подтвердить что ты не робот

Круговая зависимость spring

Как Spring разрешает это: компонент A зависит от компонента B, а компонент B - от компонента A.

4b9b3361

Ответ 1

Как говорили другие ответы, Spring просто заботится об этом, создавая beans и вставляя их по мере необходимости.

Одно из последствий заключается в том, что параметр bean injection/property может возникать в другом порядке, чем, по-видимому, подразумевают ваши файлы проводки XML. Поэтому вам нужно быть осторожным, чтобы ваши разработчики свойств не выполняли инициализацию, которая основывается на других сеттерах, которые уже были вызваны. Способ решения этой проблемы - объявить beans как реализацию интерфейса InitializingBean. Это требует, чтобы вы реализовали метод afterPropertiesSet(), и именно здесь вы выполняете критическую инициализацию. (Я также включаю код, чтобы проверить, действительно ли были установлены важные свойства.)

Ответ 2

Справочное руководство Spring объясняет, как разрешены круговые зависимости. Сначала создаются beans, затем вставляются друг в друга.

Рассмотрим этот класс:

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 еще не полностью инициализирован.

Ответ 3

В кодовой базе, с которой я работаю (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 и вручную введя beans.

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 секунд.

Поэтому не всегда предполагаем, что spring может быть полезен при решении этих ссылок для вас.

По этой причине я бы рекомендовал отключить циклическое разрешение зависимостей с помощью AbstractRefreshableApplicationContext # setAllowCircularReferences (false), чтобы предотвратить многие будущие проблемы.

Ответ 4

Он просто делает это. Он создает экземпляры a и b и вводит каждый из них в другой (используя их методы настройки).

В чем проблема?

Ответ 5

Проблема ->

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: Ошибка создания компонента с именем 'A': Запрашиваемый компонент находится в процессе создания: существует неразрешимая циклическая ссылка?

Решение 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 };
}

Ответ 6

Из Spring Ссылка:

В целом вы можете доверять Springправильная вещь. Он обнаруживает проблемы конфигурации, такие как ссылки на несуществующие beans и круговые зависимости, в контейнере время загрузки. Spring устанавливает свойства и разрешает зависимости уже возможно, когда bean на самом деле создан.

Ответ 7

Контейнер Spring способен разрешать циклические зависимости на основе Setter, но дает исключение BeanCurrentlyInCreationException во время выполнения в случае циклических зависимостей на основе конструктора. В случае циклической зависимости Setter, контейнер IOC обрабатывает его по-другому от типичного сценария, в котором он полностью настраивает сотрудничающий bean перед его инъекцией. Например, если bean A имеет зависимость от bean B и bean B от bean C, контейнер полностью инициализирует C перед введением его в B и после того, как B полностью инициализирован, он вводится в A. Но в случае круговой зависимости один из beans вводится другому, прежде чем он будет полностью инициализирован.

Ответ 8

Скажите, что A зависит от B, тогда Spring сначала создаст экземпляр A, затем B, затем задайте свойства для B, затем установите B в A.

Но что, если B также зависит от A?

Мое понимание: Spring только что нашел, что A был сконструирован (выполнен конструктор), но не полностью инициализирован (не все сделанные инъекции), ну, подумал он, хорошо, допустимо, что A не полностью инициализирован, просто установите эти не полностью инициализированные экземпляры A в B на данный момент. После того, как B полностью инициализирован, он был установлен в A, и, наконец, A было полностью инициировано сейчас.

Другими словами, он просто открывает A до B.

Для зависимостей через конструктор Sprint просто бросает BeanCurrentlyInCreationException, чтобы устранить это исключение, установите lazy-init равным true для bean, который зависит от других с помощью метода constructor-arg.

Ответ 9

Это четко объяснено здесь. Спасибо Евгению Параскиву.

Циркулярная зависимость - это запах проекта, либо исправьте его, либо используйте @Lazy для зависимости, которая вызывает проблему, чтобы обойти ее.

Ответ 10

Если вы обычно используете конструктор-инъекцию и не хотите переключиться на вложение свойств, тогда Spring lookup-method -инъекция позволит одному bean ленивому поиску другого и, следовательно, обходному пути циклическая зависимость. См. Здесь: http://docs.spring.io/spring/docs/1.2.9/reference/beans.html#d0e1161

Ответ 11

Внедрение в конструктор завершается неудачно, когда между бобами spring есть круговая зависимость. Таким образом, в этом случае мы, Setter инъекция, помогли решить проблему.

По сути, Constructor Injection полезно для обязательных зависимостей, для необязательных зависимостей лучше использовать Setter инъекцию, потому что мы можем сделать повторное внедрение.

Ответ 12

Используя инжекцию Setter или Injection Field или используя @Lazy для зависимости.

Ответ 13

Если два bean-компонента зависят друг от друга, то мы не должны использовать конструктор в обоих определениях bean-компонентов. Вместо этого мы должны использовать сеттерную инъекцию в любом из компонентов. (конечно, мы можем использовать установку сеттера n в обоих определениях bean-компонента, но инъекции конструктора в обоих генерируют исключение BeanCurrentlyInCreationException

См. Spring doc на "https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#resources-resource"