В чем разница между destroy() в бинах Singleton и Prototype?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Разница в destroy() между Singleton и Prototype
Это ключевое различие в жизненных циклах Spring бинов, которое часто вызывает путаницу. Singleton бин получает вызов destroy(), а Prototype бин — нет. Это один из самых важных моментов при проектировании управления ресурсами в Spring.
Spring Scopes
Spring имеет несколько scope'ов для бинов:
- Singleton (по умолчанию) — один экземпляр на всё приложение
- Prototype — новый экземпляр при каждом запросе
- Request/Session/Application — в веб-приложениях
Жизненный цикл Singleton
@Service
public class DatabaseConnection {
@PostConstruct
public void init() {
System.out.println("Инициализация БД");
// Открываем соединение
}
@PreDestroy
public void destroy() {
System.out.println("Закрытие БД");
// Закрываем соединение
}
}
// Uso:
// 1. Spring создаёт бин при старте
// 2. Вызывает @PostConstruct
// 3. Бин живёт до выключения приложения
// 4. При выключении Spring вызывает @PreDestroy
Гарантии: Spring ГАРАНТИРУЕТ, что @PreDestroy будет вызван ровно один раз перед выключением.
Жизненный цикл Prototype
@Service
@Scope("prototype")
public class UserProcessor {
@PostConstruct
public void init() {
System.out.println("Инициализация процессора");
}
@PreDestroy
public void destroy() {
System.out.println("Очистка процессора");
// ЭТО НЕ БУДЕТ ВЫЗВАНО!
}
}
// Uso:
// 1. Каждый раз при инъекции создаётся новый экземпляр
// 2. @PostConstruct вызывается после создания
// 3. Бин живёт в руках клиента
// 4. @PreDestroy НЕ ВЫЗЫВАЕТСЯ — контекст не отслеживает Prototype
Проблема: @PreDestroy не гарантируется для Prototype. Spring создаёт бин, но не следит за его очисткой. Это ваша ответственность!
Почему такая разница?
Singleton — один на всех. Spring создал его, значит Spring его и удалит.
Prototype — много экземпляров. Spring не может отследить все копии. Кто создал, тот и удалит.
Пример проблемы с Prototype
@Service
@Scope("prototype")
public class FileProcessor {
private FileInputStream fis;
@PostConstruct
public void init() throws IOException {
fis = new FileInputStream("data.txt");
System.out.println("Файл открыт");
}
@PreDestroy
public void destroy() throws IOException {
if (fis != null) {
fis.close();
System.out.println("Файл закрыт");
}
}
}
@Service
public class MyService {
@Autowired
private ObjectFactory<FileProcessor> processorFactory;
public void processFiles() {
FileProcessor processor = processorFactory.getObject();
processor.process();
// Забыли закрыть файл!
}
}
Решение 1: Использовать Singleton
@Service
public class FileProcessor {
// Безопасно, будет одна очистка
}
Решение 2: ObjectFactory с ручной очисткой
@Service
public class MyService {
@Autowired
private ObjectFactory<FileProcessor> processorFactory;
public void processFiles() {
FileProcessor processor = processorFactory.getObject();
try {
processor.process();
} finally {
processor.destroy();
}
}
}
Решение 3: Implements DisposableBean
@Service
@Scope("prototype")
public class FileProcessor implements DisposableBean {
private FileInputStream fis;
@PostConstruct
public void init() throws IOException {
fis = new FileInputStream("data.txt");
}
@Override
public void destroy() throws Exception {
if (fis != null) fis.close();
}
}
Таблица сравнения
| Аспект | Singleton | Prototype |
|---|---|---|
| Создание | 1 раз при старте | Каждый раз при запросе |
| @PostConstruct | Вызывается | Вызывается |
| @PreDestroy | ГАРАНТИРОВАННО | НЕ вызывается |
| Управление ресурсами | Spring | Клиент |
| Потокобезопасность | Требуется | Не требуется |
Когда использовать Prototype?
Хорошие причины:
- Stateful объекты (каждому запросу нужно своё состояние)
- Затратные ресурсы, которые долго живут в памяти
Плохие причины:
- "Может потом понадобится"
- Просто так
Итог: Singleton гарантирует вызов @PreDestroy, Prototype — нет. Это фундаментальная разница в управлении ресурсами.