← Назад к вопросам

В чем разница между destroy() в бинах Singleton и Prototype?

3.0 Senior🔥 141 комментариев
#Spring Boot и Spring Data#Spring Framework

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Разница в 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();
    }
}

Таблица сравнения

АспектSingletonPrototype
Создание1 раз при стартеКаждый раз при запросе
@PostConstructВызываетсяВызывается
@PreDestroyГАРАНТИРОВАННОНЕ вызывается
Управление ресурсамиSpringКлиент
ПотокобезопасностьТребуетсяНе требуется

Когда использовать Prototype?

Хорошие причины:

  • Stateful объекты (каждому запросу нужно своё состояние)
  • Затратные ресурсы, которые долго живут в памяти

Плохие причины:

  • "Может потом понадобится"
  • Просто так

Итог: Singleton гарантирует вызов @PreDestroy, Prototype — нет. Это фундаментальная разница в управлении ресурсами.