Этот вопрос проверяет понимание жизненного цикла prototype-бинов в Spring и различий в управлении их уничтожением по сравнению с singleton-бинами.
В Spring Framework аннотация @PreDestroy используется для пометки метода, который должен быть выполнен перед уничтожением бина. Это часть механизма обратных вызовов жизненного цикла, аналогичного @PostConstruct для инициализации. Однако её поведение кардинально отличается для бинов с разной областью видимости (scope).
Spring контейнер управляет полным жизненным циклом singleton-бинов (область видимости по умолчанию). Контейнер создаёт один экземпляр, хранит его и вызывает методы с аннотациями @PostConstruct и @PreDestroy в соответствующие моменты. При закрытии контекста (например, при остановке приложения) Spring гарантированно вызовет @PreDestroy для всех singleton-бинов, чтобы освободить ресурсы (закрыть соединения с БД, файлы и т.д.).
Для prototype-бинов (область видимости @Scope("prototype")) Spring действует иначе. Контейнер создаёт новый экземпляр бина каждый раз, когда он запрашивается через getBean() или внедряется как зависимость. После создания и внедрения зависимостей (и вызова @PostConstruct, если он есть) Spring передаёт управление объектом клиентскому коду и больше не отслеживает его. Контейнер не знает, когда клиент закончит использовать этот объект, поэтому не может автоматически вызвать метод уничтожения.
close(), shutdown() и т.п.) перекладывается на клиентский код, который создал или получил бин.Рассмотрим простой пример, демонстрирующий проблему:
@Component
@Scope("prototype")
public class PrototypeResource {
private FileInputStream fileStream;
@PostConstruct
public void init() {
System.out.println("Prototype bean created");
// Открываем ресурс
fileStream = new FileInputStream("data.txt");
}
@PreDestroy
public void cleanup() {
System.out.println("Этот метод НЕ будет вызван для prototype!");
// Потенциальная утечка ресурса
// fileStream.close();
}
// Явный метод для ручного освобождения ресурсов
public void manualCleanup() throws IOException {
if (fileStream != null) {
fileStream.close();
System.out.println("Ресурс освобождён вручную");
}
}
}Чтобы правильно работать с ресурсами в prototype-бинах, используйте следующие подходы:
DisposableBean или добавьте собственный метод (например, destroy()), который клиент должен вызывать явно.AutoCloseable, клиент может использовать его в блоке try-with-resources.Вывод: Аннотацию @PreDestroy не следует использовать для prototype-бинов, так как Spring её игнорирует. Для освобождения ресурсов prototype-бинов необходимо применять явные методы очистки, вызываемые клиентским кодом, или пересмотреть архитектуру, чтобы использовать singleton-бины с правильным управлением состоянием.