Как решить циклическую зависимость в Spring?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Циклические зависимости в Spring
Циклическая зависимость — это ситуация, когда бин A зависит от бина B, а бин B в свою очередь зависит от бина A. Spring может автоматически разрешить некоторые циклические зависимости благодаря механизму lazy initialization, но в некоторых случаях это приводит к BeanCurrentlyInCreationException.
Проблема: Циклическая зависимость
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB;
}
@Service
public class ServiceB {
@Autowired
private ServiceA serviceA;
}
Этот код вызовет ошибку: BeanCurrentlyInCreationException: Error creating bean with name serviceA.
Решение 1: Использование Setter Injection вместо Constructor Injection
Это самое простое решение. Spring поддерживает циклические зависимости при setter injection благодаря тому, что объект создаётся раньше, чем инъецируются зависимости.
@Service
public class ServiceA {
private ServiceB serviceB;
@Autowired
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
@Service
public class ServiceB {
private ServiceA serviceA;
@Autowired
public void setServiceA(ServiceA serviceA) {
this.serviceA = serviceA;
}
}
Решение 2: Использование @Lazy
@Lazy откладывает инъекцию зависимости до первого использования. Это хороший вариант, когда зависимость требуется не всегда.
@Service
public class ServiceA {
@Autowired
@Lazy
private ServiceB serviceB;
}
@Service
public class ServiceB {
@Autowired
private ServiceA serviceA;
}
Решение 3: Использование ObjectProvider
ObjectProvider позволяет отложить разрешение зависимости и обеспечивает более гибкий способ управления циклическими зависимостями.
@Service
public class ServiceA {
private final ObjectProvider<ServiceB> serviceBProvider;
@Autowired
public ServiceA(ObjectProvider<ServiceB> serviceBProvider) {
this.serviceBProvider = serviceBProvider;
}
public void doSomething() {
ServiceB serviceB = serviceBProvider.getIfAvailable();
if (serviceB != null) {
serviceB.process();
}
}
}
@Service
public class ServiceB {
@Autowired
private ServiceA serviceA;
}
Решение 4: Рефакторинг архитектуры
В идеальном случае, циклические зависимости указывают на проблемы в архитектуре. Лучше всего выделить общую функциональность в третий сервис.
@Service
public class SharedService {
// Общая логика
}
@Service
public class ServiceA {
@Autowired
private SharedService sharedService;
}
@Service
public class ServiceB {
@Autowired
private SharedService sharedService;
}
Решение 5: Использование ApplicationContext
В некоторых случаях можно использовать ApplicationContext для получения бина во время выполнения.
@Service
public class ServiceA {
@Autowired
private ApplicationContext context;
public void process() {
ServiceB serviceB = context.getBean(ServiceB.class);
serviceB.execute();
}
}
@Service
public class ServiceB {
@Autowired
private ServiceA serviceA;
}
Рекомендации
Лучше всего рефакторить архитектуру. Циклические зависимости указывают на нарушение принципа Single Responsibility. На практике используйте @Lazy или ObjectProvider для мягкого разрешения циклических зависимостей. Избегайте ApplicationContext, так как он усложняет код и делает его менее тестируемым.