Как говорили другие ответы, Spring просто заботится об этом, создавая bean-компоненты и вводя их по мере необходимости.
Одним из последствий является то, что инъекция bean-компонента / установка свойств могут происходить в порядке, отличном от того, что, по-видимому, подразумевают ваши XML-файлы проводки. Поэтому вам нужно быть осторожным, чтобы ваши установщики свойств не выполняли инициализацию, основанную на уже вызванных других установщиках. Способ справиться с этим - объявить bean-компоненты как реализующие InitializingBeanинтерфейс. Это требует от вас реализации afterPropertiesSet()метода, и именно здесь вы выполняете критическую инициализацию. (Я также включаю код, чтобы проверить, действительно ли установлены важные свойства.)
Справочное руководство Spring объясняет , как круговые зависимости разрешаются. Сначала создаются экземпляры бобов, а затем вводятся друг в друга.
Рассмотрим этот класс:
package mypackage;publicclass A {public A(){System.out.println("Creating instance of A");}private B b;publicvoid setB(B b){System.out.println("Setting property b of A instance");this.b = b;}}
И аналогичный класс B:
package mypackage;publicclass B {public B(){System.out.println("Creating instance of B");}private A a;publicvoid setA(A a){System.out.println("Setting property a of B instance");this.a = a;}}
Вот почему Spring требует конструктор без аргументов ;-)
Крис Томпсон,
15
Нет, если вы используете аргументы конструктора в определениях ваших компонентов! (Но в этом случае у вас не может быть круговой зависимости.)
Ричард Фирн,
1
@Richard Fearn Ваш пост посвящен объяснению проблемы, а не предложению решения?
gstackoverflow
4
Если вы попытаетесь использовать внедрение конструктора, org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
X. Wo
19
В кодовой базе, с которой я работаю (1 миллион + строк кода), у нас была проблема с длительным временем запуска, около 60 секунд. Мы получали 12000+ FactoryBeanNotInitializedException .
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)я напечатал исключение с условным кодом точки останова:
Интересная рекомендация. Моя встречная рекомендация заключается в том, чтобы делать это только в том случае, если вы подозреваете, что циклические ссылки вызывают проблемы с производительностью. (Было бы стыдно сломать что-то, что не нужно было ломать, пытаясь исправить проблему, которая не нуждалась в исправлении.)
Стивен К.
2
Это скользкий спуск к аду обслуживания, чтобы разрешить циклические зависимости, перепроектирование вашей архитектуры из циклических зависимостей может быть действительно сложным, как это было в нашем случае. Для нас это примерно означало, что во время запуска мы получили в два раза больше подключений к базе данных, чем фабрика сеансов была задействована в циклической зависимости. В других сценариях могли произойти гораздо более катастрофические вещи из-за того, что компонент был создан более 12000 раз. Конечно, вы должны написать свои beans так, чтобы они поддерживали их уничтожение, но зачем вообще допускать такое поведение?
jontejj
@jontejj, ты заслуживаешь печенья
serprime
14
Проблема ->
Class A {privatefinal B b;// must initialize in ctor/instance blockpublic A(B b){this.b = b };}Class B {privatefinal A a;// must initialize in ctor/instance blockpublic 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 {privatefinal B b;// must initialize in ctor/instance blockpublic A(@Lazy B b){this.b = b };}Class B {privatefinal A a;// must initialize in ctor/instance blockpublic B(A a){this.a = a };}
Как правило, вы можете доверять 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'
Ответы:
Как говорили другие ответы, Spring просто заботится об этом, создавая bean-компоненты и вводя их по мере необходимости.
Одним из последствий является то, что инъекция bean-компонента / установка свойств могут происходить в порядке, отличном от того, что, по-видимому, подразумевают ваши XML-файлы проводки. Поэтому вам нужно быть осторожным, чтобы ваши установщики свойств не выполняли инициализацию, основанную на уже вызванных других установщиках. Способ справиться с этим - объявить bean-компоненты как реализующие
InitializingBean
интерфейс. Это требует от вас реализацииafterPropertiesSet()
метода, и именно здесь вы выполняете критическую инициализацию. (Я также включаю код, чтобы проверить, действительно ли установлены важные свойства.)источник
Справочное руководство Spring объясняет , как круговые зависимости разрешаются. Сначала создаются экземпляры бобов, а затем вводятся друг в друга.
Рассмотрим этот класс:
И аналогичный класс
B
:Если бы у вас был этот файл конфигурации:
При создании контекста с использованием этой конфигурации вы увидите следующий результат:
Обратите внимание, что когда
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
где это
destroySingleton(beanName)
я напечатал исключение с условным кодом точки останова:По-видимому, это происходит, когда FactoryBean участвуют в циклическом графе зависимостей. Мы решили это, реализовав ApplicationContextAware и InitializingBean и вручную внедрив bean-компоненты.
Это сократило время запуска примерно до 15 секунд.
Поэтому не всегда предполагайте, что весна может помочь вам решить эти проблемы.
По этой причине я бы рекомендовал отключить разрешение циклических зависимостей с помощью AbstractRefreshableApplicationContext # setAllowCircularReferences (false), чтобы предотвратить многие проблемы в будущем.
источник
Проблема ->
// Вызвано: org.springframework.beans.factory.BeanCurrentlyInCreationException: Ошибка при создании bean-компонента с именем 'A': Запрошенный bean-компонент в настоящее время находится в создании: есть ли неразрешимая циклическая ссылка?
Решение 1 ->
Решение 2 ->
источник
Он просто делает это. Он создает
a
иb
и вставляет каждый в другой (используя свои методы установки).В чем проблема?
источник
Из ссылки 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.
источник
Это ясно объяснено здесь . Спасибо Евгению Паращеву.
Циклическая зависимость - это запах дизайна, либо исправьте его, либо используйте @Lazy для зависимости, которая вызывает проблему, чтобы ее обойти.
источник
Если вы обычно используете инъекцию конструктора и не хотите переключаться на инъекцию свойств, тогда инъекция метода поиска 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 »
источник