Какие методы из PostConstruct и PreDestroy выполняются у прототипных бинов в Spring
Что проверяют
Понимаете ли вы, какую часть жизненного цикла бина контейнер берёт на себя в разных скоупах. Следом обычно спрашивают, что будет, если внедрить прототип в синглтон, и как всё-таки освободить ресурсы прототипа.
Как отвечать
Короткий ответ: у прототипного бина выполняется @PostConstruct, а @PreDestroy контейнер не вызывает.
@PostConstruct: вызывается для каждого нового экземпляра после внедрения зависимостей. Прототип создаётся при каждомgetBean(), каждом внедрении в другой бин и каждомObjectProvider.getObject(), и каждый раз срабатывает инициализация;@PreDestroy: не вызывается. Контейнер создаёт прототип, настраивает, отдаёт клиенту и больше его не отслеживает, при закрытии контекста ему нечего уничтожать. То же касаетсяDisposableBean.destroy()иdestroy-method;- синглтон для сравнения: срабатывают оба метода, инициализация при старте контекста, уничтожение при
close(); - request и session:
@PreDestroyвызывается в конце запроса или сессии.
Как освободить ресурсы прототипа: вызвать метод очистки самому, сделать прототип AutoCloseable и использовать try-with-resources, вызвать destroyBean() у ConfigurableBeanFactory или AutowireCapableBeanFactory, либо написать BeanPostProcessor, который запоминает созданные экземпляры.
Порядок колбэков стоит знать целиком: при инициализации @PostConstruct, затем afterPropertiesSet(), затем init-method; при уничтожении @PreDestroy, destroy(), destroy-method. В Spring 6 и Boot 3 аннотации берутся из пакета jakarta.annotation.
Где спотыкаются
Думают, что @PreDestroy вызовется при сборке мусора. Сборщик ничего не знает об аннотациях Spring, и файл или соединение останутся открытыми.
Внедряют прототип в синглтон через поле и ждут новый экземпляр на каждый вызов. Внедрение происходит один раз, нужен ObjectProvider<T>, метод с @Lookup или scoped proxy.
Считают, что прототип создаётся при старте контекста, как синглтон. Он создаётся по запросу, поэтому ошибка в его @PostConstruct проявится не при запуске, а при первом getBean() или создании бина, который от него зависит.