Столкнулись ли с цикличными зависимостями бинов
Что проверяют
Проверяют, понимаете ли вы порядок создания бинов в Spring и видите ли за технической ошибкой проблему дизайна. Ответ «поставил @Lazy, и заработало» принимают, но помечают: кандидат умеет гасить симптом. Хороший ответ начинается с того, почему два сервиса вообще стали звать друг друга.
Как отвечать
Объясните механику: контейнер создаёт бин, для конструктора ему нужны все зависимости сразу, а они ещё не готовы, отсюда BeanCurrentlyInCreationException. При инъекции через поле или сеттер объект успевает попасть в кэш недособранных синглтонов, поэтому цикл раньше разрешался сам собой и годами жил в проекте незамеченным. В свежих сборках Spring Boot циклические ссылки запрещены по умолчанию, а флаг spring.main.allow-circular-references=true стоит воспринимать как временную меру, а не как решение.
Дальше перечислите варианты по возрастанию честности:
- вынести общую логику в третий бин, от которого зависят оба: цикл исчезает вместе с причиной;
- перевернуть зависимость через событие: тот, кто был вызывающим, публикует событие, второй его слушает;
- получать зависимость лениво через
ObjectProviderв момент вызова, а не в конструкторе; @Lazyна параметре конструктора: контейнер подставит прокси и создаст настоящий бин при первом обращении.
Скажите отдельно, что при инъекции через конструктор цикл виден сразу на старте, и это аргумент в её пользу, а не недостаток.
Где спотыкаются
Считают, что выделение интерфейса разрывает цикл. Не разрывает: зависимость остаётся той же, меняется только её тип, а контейнер по-прежнему подставляет ту же реализацию. Помогает не интерфейс сам по себе, а перенос ответственности в другой бин.
Ставят @Lazy наугад и получают падение позже, уже под нагрузкой, в момент первого обращения к прокси. В стеке при этом видно случайное место вызова, а не сборку контекста, и связать одно с другим тяжело.
Достают зависимость вручную через ApplicationContext внутри метода. Цикл никуда не делся, он просто перестал быть виден контейнеру, и теперь связь между бинами нигде не объявлена, а значит не проверяется ни на старте, ни в тестах.