← Назад к вопросам

Как решить циклическую зависимость в Spring?

2.0 Middle🔥 111 комментариев
#Spring Boot и Spring Data#Spring Framework

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Циклические зависимости в 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, так как он усложняет код и делает его менее тестируемым.