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 }; }
Если два компонента зависят друг от друга, то мы не должны использовать инъекцию конструктора в обоих определениях компонентов. Вместо этого мы должны использовать инъекцию сеттера в любой из бобов. (конечно, мы можем использовать инъекцию 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