Круговая зависимость весной



Как Весна разрешить этот: фасоль в зернах зависит от B, А B на Бин А. бин

509   13  

13 ответов:

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

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

рассматривать этот класс:

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

в кодовой базе, с которой я работаю (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;

по-видимому, это происходит, когда FactoryBeans участвуют в циклическом графе зависимостей. Мы решили ее, реализовав 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 (false) для предотвращения многих будущих проблем.

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

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

с Весна Ссылка:

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

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

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

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

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

говорят, что а зависит от Б, то весна будет сначала создать, а потом B, затем установите свойства для Б, то Б В А.

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

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

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

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

>

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.зернышки.фабрика.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 };
}

с помощью инъекции сеттера или инъекции поля или с помощью @Lazy для зависимости.

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

обратитесь к Spring doc at "https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#resources-resource"

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

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

Comments

    Ничего не найдено.