Что такое ленивый (lazy) бин в Spring?

Ответ

Ленивый (lazy) бин — это компонент Spring, инициализация которого откладывается до момента его первого запроса, а не выполняется при запуске контекста приложения.

Зачем это нужно?

  • Оптимизация старта: Ускоряет запуск приложения, если бин ресурсоемкий (например, подключается к внешним сервисам, загружает большие файлы).
  • Условная инициализация: Полезен, когда бин требуется не во всех сценариях работы приложения.

Как объявить? Используйте аннотацию @Lazy:

@Component
@Lazy // Инициализация произойдет только при первом инжекте или вызове
public class HeavyResourceService {
    public HeavyResourceService() {
        // Долгая инициализация
        System.out.println("HeavyResourceService создан!");
    }
}

Важные детали:

  1. Поведение по умолчанию: Бины в Spring — eager (жадные), они создаются при старте контекста.
  2. Область применения: @Lazy можно ставить на класс (@Component, @Service и т.д.) или на метод, объявляющий бин (@Bean).
  3. Зависимости: Если ленивый бин является зависимостью другого бина, аннотацию @Lazy нужно также указать и в точке инжекции (в поле, конструкторе или методе-сеттере), иначе Spring создаст его при создании зависимого бина.
  4. Скоупы: Для бинов со скоупом prototype поведение по умолчанию всегда аналогично ленивой инициализации.

Ответ 18+ 🔞

А, ну вот, смотри, ленивый бин в Спринге — это как тот твой друг, который всегда говорит «щас, щас приду», а на деле вылезает из дома, только когда все уже в баре сидят и третью кружку допивают. Понимаешь? Это бин, который нихуя не делается, пока его впервые реально не попросят.

А нахуя это вообще надо, спросишь ты?

  • Чтобы приложение не тупило на старте. Представь, у тебя есть сервис, который подключается к какому-нибудь древнему медленному API, или там гигабайтный конфиг грузит. Зачем его поднимать сразу, если пользователь, может, даже на эту страницу не зайдет? Пусть валяется, пока не понадобится.
  • Если он нужен не всегда. Ну, типа, функционал «отправить отчет бухгалтерии в налоговую» — его раз в квартал дергают, а приложение-то крутится каждый день. Зачем его грузить?

Как его такого объявить? Вешаешь волшебную аннотацию @Lazy, и всё.

@Component
@Lazy // Вот этот флажок. Теперь он будет сидеть в углу, пока к нему не обратятся.
public class HeavyResourceService {
    public HeavyResourceService() {
        // Тут типа долгая инициализация, все дела
        System.out.println("HeavyResourceService создан! Ну наконец-то, блядь.");
    }
}

А теперь, блядь, важные нюансы, чтобы не обосраться:

  1. По дефолту все жадные. Спринг как голодный студент — хватает все бины сразу и пытается их скормить контексту. @Lazy делает бин нежадным, похуистичным.
  2. Куда вешать можно. Или прямо на класс (@Component, @Service), или на метод с @Bean в конфиге. Где удобнее.
  3. Вот это, блядь, ключевое — зависимости! Допустим, у тебя есть UserService, который использует этот твой ленивый HeavyResourceService. Так вот, мало @Lazy на самом HeavyResourceService навесить. Надо его и в UserService при инжекте тоже как ленивую зависимость пометить! Иначе Спринг такой: «Ага, создаю UserService, а для него нужен HeavyResourceService... Так, а он у нас ленивый... Ну похуй, создам сейчас же!». И вся оптимизация коту под хвост.
  4. С бинами-прототипами (prototype) вообще отдельная история. Они по умолчанию и так ведут себя как ленивые, потому что их создают новый экземпляр на каждый запрос. Так что там @Lazy обычно не нужен.

Короче, инструмент охуенный, но, как и всё в Спринге, требует, чтобы ты немного подумал головой, а не просто аннотации тыкал. Иначе получишь не оптимизацию, а ебучую головную боль, когда что-то не создалось тогда, когда ты ожидал.