В каких ситуациях не срабатывает аннотация PreDestroy в Java
Что проверяют
Понимаете ли вы, кто и когда вызывает @PreDestroy: не JVM, а контейнер при закрытии контекста. Следом обычно спрашивают, как гарантированно освободить ресурс, если приложение падает или его останавливает Kubernetes.
Как отвечать
Метод с @PreDestroy вызывает контейнер (Spring, CDI), когда закрывает контекст и уничтожает бины. Отсюда все случаи, когда он не сработает:
- объект не управляется контейнером: создан через
new, аннотация на нём ничего не делает; - scope
prototype: Spring создаёт такой бин и отдаёт, но жизненный цикл до конца не ведёт, колбэки уничтожения не вызываются. Чистить ресурсы должен тот, кто взял бин; - контекст не закрыт: в обычном
mainбезcontext.close()илиregisterShutdownHook(). Spring Boot регистрирует shutdown hook сам, поэтому там это работает по умолчанию; - JVM убита жёстко:
kill -9,Runtime.halt(), аварийное падение процесса. Shutdown hooks не выполняются, и@PreDestroyтоже; - Docker и Kubernetes: при остановке приходит SIGTERM, а через
terminationGracePeriodSeconds(по умолчанию 30 секунд) SIGKILL. Если Java запущена из shell-скрипта безexec, сигнал до неё не дойдёт, и процесс умрёт по SIGKILL; - неверная сигнатура или импорт: метод должен быть нестатическим и без параметров. С JDK 11 пакет
javax.annotationубран из JDK, в Spring Boot 3 и Jakarta EE используютjakarta.annotation.PreDestroy.
Итог: @PreDestroy годится для аккуратного завершения, но не для гарантий. Критичное состояние делайте устойчивым к внезапной смерти процесса: транзакции, идемпотентность, повторная обработка.
Где спотыкаются
Говорят, что System.exit() не вызывает @PreDestroy. На самом деле он запускает shutdown hooks, и если хук Spring зарегистрирован, контекст закроется и методы отработают.
Вешают @PreDestroy на prototype-бин и удивляются незакрытым соединениям.
Считают, что исключение в одном @PreDestroy остановит уничтожение остальных бинов. Spring пишет ошибку в лог и продолжает, но ресурс этого бина остаётся неосвобождённым.