Каков жизненный цикл контекста в Spring Framework?

Ответ

Жизненный цикл контекста Spring (ApplicationContext) включает следующие ключевые этапы:

  1. Загрузка конфигурации и создание BeanFactory — чтение конфигурации (XML, аннотации, JavaConfig) и создание BeanDefinition для каждого бина.
  2. Создание экземпляров бинов (Instantiation) — контейнер создает экземпляры бинов в соответствии с их scope (singleton, prototype и др.).
  3. Внедрение зависимостей (Dependency Injection) — разрешение и инъекция зависимостей через конструктор, сеттеры или поля.
  4. Обработка callback-интерфейсов и аннотаций жизненного цикла — выполнение методов, помеченных как @PostConstruct, или реализующих InitializingBean.afterPropertiesSet().
  5. Обработка BeanPostProcessor — выполнение пользовательской логики до и после инициализации каждого бина (например, для проксирования через AOP).
  6. Готовность (Ready) — контекст полностью инициализирован, бины готовы к использованию.
  7. Уничтожение (Destruction) — при закрытии контекста (context.close()) вызываются методы @PreDestroy или DisposableBean.destroy().

Пример конфигурации с явным указанием методов жизненного цикла:

@Configuration
public class AppConfig {
    @Bean(initMethod = "customInit", destroyMethod = "customCleanup")
    public DataSource dataSource() {
        return new HikariDataSource();
    }
}

public class MyService {
    @PostConstruct
    public void init() {
        // Логика инициализации после инъекции зависимостей
    }
    @PreDestroy
    public void cleanup() {
        // Логика освобождения ресурсов перед уничтожением
    }
}

Почему это важно: Понимание жизненного цикла позволяет корректно управлять ресурсами (открытие/закрытие соединений), выполнять валидацию конфигурации и внедрять кастомную логику через BeanPostProcessor.

Ответ 18+ 🔞

А, слушай, про жизненный цикл в Spring, да? Ну это, блядь, как у человека: родился, поел, поработал, помер. Только тут бины, эти, ёпта, деревянные.

Вот смотри, как этот цирк с контекстом работает, по пунктам, а то мозг сейчас ебнешься.

  1. Загрузка конфигурации. Это как принести из магазина инструкцию по сборке шкафа. Контейнер читает твой XML, JavaConfig или эти ёбаные аннотации, и для каждого бина создаёт чертёж — BeanDefinition. Пока ещё ничего не собрано, просто бумажки лежат, понимаешь?

  2. Создание экземпляров. А вот тут уже начинается магия. Контейнер берёт чертёж и начинает, блядь, собирать шкаф. То есть создаёт объекты. Singleton? Один на всех, жадный или ленивый. Prototype? Каждый раз новый, как одноразовые стаканчики. Просто инстанциирует, поля пустые, зависимости не проставлены — полный пиздец, одним словом.

  3. Внедрение зависимостей. А это самый сок, ради чего всё и затевалось. Контейнер смотрит: ага, этому бину нужен другой бин в конструктор, этому — через сеттер, а этот чувак просто пометил поле аннотацией @Autowired и ждёт, что ему всё само впрыгнет. И начинает, сука, впендюривать эти зависимости куда надо. Как пазл собирает. Если чего-то не хватает — овердохуища ошибок и BeanCreationException тебе в сраку.

  4. Callback'и инициализации. Ну вот, бин почти готов. И тут Spring такой: "О, а у тебя же есть метод с @PostConstruct! Или ты там InitializingBean реализовал!". И вызывает эту пользовательскую хуйню — типа, "эй, чувак, ты собран, можешь проверить, всё ли у тебя на месте, подключиться к базе, отправить сигнал готовности". Важный этап, блядь.

  5. Обработка BeanPostProcessor. А это, ёпта, высший пилотаж. Это такие хитрожопые перехватчики, которые могут накостылить что-то своё ДО и ПОСЛЕ инициализации каждого бина. Хочешь обернуть бин в прокси для AOP? Пожалуйста. Хочешь заменить его на какую-нибудь мок-заглушку в тестах? Да без проблем. Мощнейшая хуйня, но если накосячить — пидарас шерстяной, всё поломаешь.

  6. Готовность. Всё, пиздец, контекст поднят. Все бины созданы, зависимости впендюрены, пост-процессоры отработали. Можно вызывать context.getBean(...) и использовать эту красоту. Приложение работает, трафик идёт, бабки капают. Идиллия, блядь.

  7. Уничтожение. А вот и финал, как в хорошей трагедии. Закрываешь контекст (context.close()), и Spring начинает аккуратно всё убивать. Вызывает методы с @PreDestroy, или у кого DisposableBean реализован. Чтобы соединения к базе закрыть, файлы сохранить, потоки остановить. А то без этого — утечка памяти, и приложение как собака, блядь, сдохнет, не попрощавшись.

Вот, например, как это в коде выглядит, смотри:

@Configuration
public class AppConfig {
    @Bean(initMethod = "customInit", destroyMethod = "customCleanup")
    public DataSource dataSource() {
        return new HikariDataSource(); // Пул соединений, серьёзная хуйня
    }
}

public class MyService {
    @PostConstruct
    public void init() {
        // Тут типа "ой, я родился, всё проверил, вроде живой"
    }
    @PreDestroy
    public void cleanup() {
        // А тут "всё, пиздец, приехали, закрываю за собой все двери"
    }
}

А нахуя это всё знать-то? Да затем, чувак, чтобы не быть, извини, идиотом. Хочешь правильно открыть и закрыть сокет или подключение к БД? Жизненный цикл. Хочешь сделать кастомную валидацию бина после его создания? Жизненный цикл. Хочешь через AOP навесить логирование на все сервисы? BeanPostProcessor, который является частью этого ёбаного цикла. Без этого — как слепой котёнок, тыкаешься мордой в код и удивляешься, почему NullPointerException вылезает из ниоткуда. Вот так вот, в рот меня чих-пых.