В прошлом посте мы подробно разобрали, как
@Lazy помогает экономить ресурсы и ускорять старт приложения. Но забыли упомянуть ещё один крайне полезный кейс применения этой аннотации — борьбу с циклическими зависимостями.Если в приложении бин A зависит от бина B, а бин B в свою очередь зависит от A — вы получите классическую circular dependency. Spring просто не сможет создать такие бины через конструкторы. Однако, если применить
@Lazy на одном из аргументов, Spring обернёт зависимость в прокси и разорвёт цикл.
@Service
public class ServiceA {
private final ServiceB serviceB;
public ServiceA(@Lazy ServiceB serviceB) {
this.serviceB = serviceB;
}
}
Важно:
@Lazy здесь влияет только на точку инжекции, а не на весь бин. Оба бина будут инициализированы жадно, но зависимость будет подгружена позже.Если же вы хотите, чтобы инициализация бина проходила лениво (при первом обращении к бину), то отметьте аннотацией
@Lazy и сам бин тоже:
@Lazy
@Service
public class ServiceB {
// ...
}
⚠️ Использование свойства:
spring.main.allow-circular-references=trueЭто ещё один способ разрешить циклы на уровне конфигурации. Spring сам предложит это в логах, если столкнётся с циклом:
Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.
Но будьте осторожны:
– Это работает только с field-based и setter-based инжекцией
– Если используете constructor-based инжекцию, то приложение по прежнему не запустится, лишь немного изменится сообщение в логах
– До Spring Boot 2.6 это поведение было включено по умолчанию — после обновления многие столкнулись с неожиданными фейлами.
Поэтому такой подход стоит рассматривать как временную меру, а не как архитектурное решение.
#SpringTips #Lazy