Что будет при инъекции бина А в бин В, а бина В в бин А?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Циклические зависимости в Spring (Circular Dependency)
Это классический вопрос на собеседовании, и я сталкивался с этим не раз в real-world проектах. Когда бин А зависит от бина В, а бин В зависит от бина А, возникает циклическая зависимость.
Что происходит?
Spring контейнер обнаруживает эту циклическую зависимость и по умолчанию автоматически её разрешает, но поведение зависит от типа инъекции.
Scenario 1: Constructor Injection (Инъекция через конструктор)
Это самый опасный случай, который приводит к ошибке при старте приложения.
@Component
public class BeanA {
private final BeanB beanB;
// Constructor injection - циклическая зависимость!
public BeanA(BeanB beanB) {
this.beanB = beanB;
}
}
@Component
public class BeanB {
private final BeanA beanA;
public BeanB(BeanA beanA) {
this.beanA = beanA;
}
}
Результат: BeanCurrentlyInCreationException
The dependencies of some of the beans in the application context form a cycle:
beanA -> beanB -> beanA
Почему так происходит?
- Spring пытается создать BeanA
- Для BeanA нужен BeanB (требуется конструктор)
- Для BeanB нужен BeanA (требуется конструктор)
- Бесконечный цикл - Spring выбрасывает исключение
Scenario 2: Setter Injection (Инъекция через setter)
Это работает благодаря двухфазному созданию объектов.
@Component
public class BeanA {
private BeanB beanB;
@Autowired
public void setBeanB(BeanB beanB) {
this.beanB = beanB;
}
}
@Component
public class BeanB {
private BeanA beanA;
@Autowired
public void setBeanA(BeanA beanA) {
this.beanA = beanA;
}
}
Результат: Приложение стартует успешно!
Почему это работает:
- Spring создаёт BeanA без зависимостей (вызывает конструктор)
- Spring создаёт BeanB без зависимостей (вызывает конструктор)
- Затем Spring вызывает setters и устанавливает зависимости
- Циклическая зависимость разрешена через setters
Scenario 3: Field Injection (@Autowired на поле)
Работает аналогично setter injection.
@Component
public class BeanA {
@Autowired
private BeanB beanB; // Инъекция после создания
}
@Component
public class BeanB {
@Autowired
private BeanA beanA; // Инъекция после создания
}
Результат: Приложение стартует успешно!
Но это bad practice по разным причинам.
Как разрешить циклическую зависимость?
Вариант 1: Использовать ObjectProvider (рекомендуется)
@Component
public class BeanA {
private final ObjectProvider<BeanB> beanBProvider;
public BeanA(ObjectProvider<BeanB> beanBProvider) {
this.beanBProvider = beanBProvider; // Lazy resolution
}
public void doSomething() {
BeanB beanB = beanBProvider.getIfAvailable();
// Используем beanB
}
}
Преимущества:
- Работает с constructor injection
- Lazy loading - зависимость разрешается позже
- Type-safe
Вариант 2: Использовать Lazy
@Component
public class BeanA {
private final Lazy<BeanB> beanB;
public BeanA(Lazy<BeanB> beanB) {
this.beanB = beanB;
}
public void work() {
BeanB actual = beanB.get(); // Получить при необходимости
}
}
Вариант 3: Рефакторинг дизайна (Best Practice)
Если у вас циклическая зависимость - это часто признак того, что архитектура неправильная.
// Выделить общий интерфейс
public interface Service {
void process();
}
@Component
public class ServiceA implements Service {
// Не зависит от ServiceB
}
@Component
public class ServiceB {
private final Service service; // Зависит от интерфейса
public ServiceB(Service service) {
this.service = service;
}
}
Мой подход в production
В настоящих проектах я избегаю циклических зависимостей:
- Первый выбор: Рефакторинг архитектуры
- Второй выбор: ObjectProvider для ленивого разрешения
- Третий выбор: EventPublisher pattern для декаплинга
Event-driven подход:
@Component
public class BeanA {
private final ApplicationEventPublisher publisher;
public BeanA(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public void notify() {
publisher.publishEvent(new MyEvent()); // Не зависит от BeanB
}
}
@Component
public class BeanB {
@EventListener
public void onEvent(MyEvent event) {
// Слушаем события от BeanA
}
}
Этот подход полностью устраняет циклическую зависимость и делает компоненты более независимыми.