Як весна вирішує це: квасоля A залежить від квасолі B, а квасоля B - квасоля А.
Як весна вирішує це: квасоля A залежить від квасолі B, а квасоля B - квасоля А.
Відповіді:
Як сказано в інших відповідях, Весна просто дбає про це, створюючи боби і впорскуючи їх у міру необхідності.
Одним із наслідків є те, що інжекція / властивість бобів може відбуватися в іншому порядку, ніж те, що, здавалося б, передбачає ваші XML-файли електропроводки. Тому вам слід бути обережними, щоб ваші налаштування властивостей не зробили ініціалізацію, яка покладається на інші вже встановлені дзвінки. Спосіб вирішити це - оголосити боби як реалізацію 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 s залучаються до циклічного графіка залежності. Ми вирішили це, застосувавши 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 (помилковим), щоб запобігти багатьом майбутнім проблемам.
Проблема ->
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": Запитаний боб наразі створюється: Чи існує нерозв'язна кругова посилання?
Рішення 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 здатний вирішувати кругові залежності на основі Setter, але дає виняток під час виконання BeanCurrentlyInCreationException у разі кругових залежностей на основі Constructor. У разі кругової залежності на основі сеттера контейнер IOC обробляє його інакше, ніж типовий сценарій, в якому він повністю налаштував би співпрацюючий боб перед його введенням. Наприклад, якщо Bean A має залежність від Bean B і Bean B від Bean C, контейнер повністю ініціалізує C перед введенням його в B, і як тільки B буде повністю ініціалізований, його вводять до A. Але у випадку кругової залежності квасолі вводять іншу до її повної ініціалізації.
Скажіть, A залежить від B, тоді Spring спочатку інстанціює A, потім B, потім встановить властивості для B, потім встановить B на A.
Але що робити, якщо B також залежить від A?
Я розумію: Весна щойно виявила, що A побудований (конструктор виконаний), але не повністю ініціалізований (не всі ін'єкції зроблено), ну, він вважав, що це нормально, допустимо, що A не повністю ініціалізовано, просто встановіть це не- повністю ініціалізовані екземпляри A на даний момент. Після того, як B повністю ініціалізований, він був встановлений у A, і нарешті, A був повністю ініційований зараз.
Іншими словами, воно просто заздалегідь виставляє А до В.
Для залежностей через конструктор Sprint просто кинемо BeanCurrentlyInCreationException, щоб вирішити цей виняток, встановивши lazy-init на істинне для bean, що залежить від інших, через конструктор-arg спосіб.
Якщо ви, як правило, використовуєте конструктор-ін'єкцію та не хочете переходити до властивостей-ін'єкцій, тоді метод пошуку Spring -ін'єкція дозволить одному бобі лінько шукати інший і, отже, обходити циклічну залежність. Дивіться тут: http://docs.spring.io/spring/docs/1.2.9/reference/beans.html#d0e1161
Інжекція конструктора не вдається, коли між пружинистими бобами існує кругова залежність. Тож у цьому випадку ми ін’єкція сетера допомагає вирішити проблему.
В основному, інжекція конструктора корисна для обов`язкових залежностей, для необов'язкових залежностей краще використовувати ін'єкцію Setter, оскільки ми можемо зробити повторну ін'єкцію.
Якщо дві квасолі залежать одна від одної, тоді ми не повинні використовувати ін'єкцію Constructor в обох визначеннях квасолі. Натомість нам доводиться використовувати ін'єкцію сетера в будь-яку з квасолі. (звичайно, ми можемо використовувати інжектор сетерів n обох визначень боба, але конструкторські ін'єкції в обох кидках "BeanCurrentlyInCreationException"
Зверніться до весняного документа на сторінку " https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#resources-resource "