Вызывается ли метод с аннотацией @PreDestroy у бинов с областью видимости prototype в Spring?

Ответ

Нет, метод @PreDestroy не вызывается Spring-контейнером для бинов с областью видимости prototype.

Причина: Spring управляет полным жизненным циклом только для singleton бинов. Для бинов prototype контейнер отвечает только за их создание и инициализацию (вызов @PostConstruct), после чего передаёт экземпляр клиенту. Уничтожение prototype бина и освобождение его ресурсов — это ответственность клиентского кода.

Пример:

@Component
@Scope("prototype")
public class PrototypeBean {
    @PostConstruct
    public void init() {
        System.out.println("Prototype bean initialized."); // Вызовется
    }

    @PreDestroy
    public void cleanup() {
        System.out.println("This will NOT be called by Spring!"); // Не вызовется
    }
}

Альтернативы для освобождения ресурсов:

  1. Реализовать интерфейс DisposableBean (не рекомендуется из-за связывания с Spring API).
  2. Использовать кастомный метод уничтожения в @Bean определении: @Bean(destroyMethod="myCleanup").
  3. Вызывать метод очистки явно в клиентском коде, когда работа с бином завершена.
  4. Использовать шаблон «callback»: определить собственный интерфейс очистки, который клиент будет вызывать.

Ответ 18+ 🔞

Да ты посмотри, какая дичь, блядь! Вот сидишь ты, такой весь в себе, думаешь: «А, Spring, блядь, умный фреймворк, всё за меня сделает, вызовет, почистит, в рот меня чих-пых!». Ан хуй там, а не везде!

Смотри сюда, распиздяй. Берёшь ты бин, накручиваешь на него @Scope("prototype"). Ну, чтобы он каждый раз новый, как девственница, блядь, свежий и незаезженный. И пишешь ты ему два метода: один @PostConstruct — чтобы инициализировался, красавчик, а второй @PostDestroy — чтобы, значит, на выходе прибрался, как порядочный.

И что же выходит? А выходит пиздец, вот что! Spring, этот хитрожопый контейнер, для prototype бина делает только полдела. Он его родит, вызовет @PostConstruct (тут всё честно), и — хуяк! — суёт тебе в руки, мол, «на, распидорасил, дальше сам разбирайся». А про то, чтобы его убить и @PreDestroy вызвать, он нихуя не думает! Совсем! Ответственность, блядь, перекладывает.

Вот смотри на код, небось думал, что всё сработает:

@Component
@Scope("prototype")
public class PrototypeBean {
    @PostConstruct
    public void init() {
        System.out.println("Prototype bean initialized."); // О, это да, это он позовёт!
    }

    @PreDestroy
    public void cleanup() {
        System.out.println("This will NOT be called by Spring!"); // А вот это — нет, сука! Никогда!
    }
}

Вот и сиди теперь с этим prototype бином, как дурак. Он тебе память жрёт, ресурсы держит, а Spring только плечами пожимает: «Не моя, мол, зона, чувак. Я свою работу сделал — создал».

Ну и что делать-то, спрашивается? Сидеть и плакать? Да нет же, ёпта! Есть же варианты, хоть и кривожопые:

  1. DisposableBean — интерфейс этот старый, дедовский. Реализуешь его, переопределяешь destroy(). Но это ж связывание с API Spring, блядь! Не комильфо.
  2. Кастомный destroyMethod в @Bean — если объявляешь бин через Java-конфиг, можно ткнуть пальцем: @Bean(destroyMethod="myCleanup"). Скажешь ему, мол, вот этот метод, блядь, дергай на уничтожение. Но опять же, для prototype — хер ты чего добьёшься, если контейнер им не управляет.
  3. Сам, блядь, вызови! — Вот самый честный путь. Когда клиентский код заканчивает с этим бином работать, он должен явно, как мужчина, позвать метод очистки. Типа prototypeBean.cleanup(). Ответственность, сука, на тебе.
  4. Шаблон «колбэк» — Сделаешь свой интерфейсик, Cleanable там какой-нибудь. Бин его реализует, а клиент, когда надо, дергает clean(). Красиво, но мороки — овердохуища.

Короче, вывод простой, как три рубля: prototype бин — это как гость в твоей квартире. Spring его только в дверь запустит (@PostConstruct), а выпроваживать (@PreDestroy) — это уже твоя, блядь, головная боль. Не забывай об этом, а то ресурсы по углам протекать начнут!