← Назад к вопросам
Выполнится ли метод с аннотацией PreDestroy у Bean с Prototype Scope в Spring?
2.3 Middle🔥 231 комментариев
#Spring Framework
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# @PreDestroy в Bean с Prototype Scope в Spring
Краткий ответ
НЕТ, метод с @PreDestroy НЕ выполнится для Bean с Prototype Scope. Spring управляет только созданием prototype bean'ов, но не их уничтожением.
Причины
Жизненный цикл Prototype
Prototype Bean
spring.getBean("myBean")
↓
[CREATE]
[INIT] (@PostConstruct выполнится)
↓
Возвращение bean'а
↓
Приложение управляет
(Spring отпускает управление)
↓
[???? - DESTROY]
(@PreDestroy НЕ выполнится)
Жизненный цикл Singleton (для сравнения)
Singleton Bean (по умолчанию)
spring.getBean("myBean")
↓
[CREATE]
[INIT] (@PostConstruct выполнится)
↓
Spring управляет
↓
При shutdown контекста:
[DESTROY] (@PreDestroy выполнится)
Пример: разница в поведении
@Component
@Scope(BeanScope.PROTOTYPE)
public class PrototypeBean {
@PostConstruct
public void init() {
System.out.println("PrototypeBean init");
}
@PreDestroy
public void destroy() {
System.out.println("PrototypeBean destroy"); // НЕ ВЫПОЛНИТСЯ!
}
}
@Component
@Scope(BeanScope.SINGLETON) // По умолчанию
public class SingletonBean {
@PostConstruct
public void init() {
System.out.println("SingletonBean init");
}
@PreDestroy
public void destroy() {
System.out.println("SingletonBean destroy"); // ВЫПОЛНИТСЯ!
}
}
// Использование
public class Application {
public static void main(String[] args) {
ApplicationContext context = new SpringApplication.run(Application.class, args);
// Singleton: init выполнится при создании контекста
// SingletonBean init
// Prototype: init выполнится при запросе
PrototypeBean proto1 = context.getBean(PrototypeBean.class);
// PrototypeBean init
PrototypeBean proto2 = context.getBean(PrototypeBean.class);
// PrototypeBean init (новый экземпляр)
// При закрытии контекста:
context.close();
// SingletonBean destroy
// PrototypeBean destroy НЕ выполнится!
}
}
Почему это происходит?
Управление жизненным циклом
Spring управляет жизненным циклом только Singleton и Session/Request bean'ов.
Облаганизация управления Spring:
SINGLETON: CREATE → INIT → использование → DESTROY
↑ ↑
Spring управляет Spring управляет
PROTOTYPE: CREATE → INIT → использование → DESTROY
↑ ↑ ↑
Spring Spring Приложение
управляет управляет управляет
Причины дизайна
- Управление памятью: Prototype bean'ов может быть много, приложение должно управлять их очисткой
- Множественные экземпляры: У приложения есть ссылка на bean, Spring не знает, когда его удалять
- Ответственность: Если создал объект, ты ответственен за его очистку
Как работает с другими scope'ами
// SINGLETON — выполнится ✓
@Scope("singleton")
@Component
public class SingletonComponent {
@PreDestroy
public void cleanup() { } // Выполнится
}
// REQUEST — выполнится ✓
@Scope("request")
@Component
public class RequestComponent {
@PreDestroy
public void cleanup() { } // Выполнится при завершении request
}
// SESSION — выполнится ✓
@Scope("session")
@Component
public class SessionComponent {
@PreDestroy
public void cleanup() { } // Выполнится при завершении session
}
// PROTOTYPE — НЕ выполнится ✗
@Scope("prototype")
@Component
public class PrototypeComponent {
@PreDestroy
public void cleanup() { } // НЕ выполнится
}
Таблица scope'ов
| Scope | Управление | Create | Init | Destroy |
|---|---|---|---|---|
| Singleton | Spring | Spring | Spring | Spring ✓ |
| Prototype | Приложение | Spring | Spring | ✗ |
| Request | Spring | Spring | Spring | Spring ✓ |
| Session | Spring | Spring | Spring | Spring ✓ |
| WebSocket | Spring | Spring | Spring | Spring ✓ |
| Application | Spring | Spring | Spring | Spring ✓ |
Как обойти эту проблему
Вариант 1: Использовать DisposableBean
@Component
@Scope("prototype")
public class PrototypeBean implements DisposableBean {
@PostConstruct
public void init() {
System.out.println("Init");
}
@Override
public void destroy() throws Exception {
// Всё равно НЕ выполнится для Prototype!
System.out.println("Destroy");
}
}
Вариант 2: ObjectFactory для управления
@Component
public class PrototypeManager {
@Autowired
private ObjectFactory<PrototypeBean> beanFactory;
public void usePrototypeBean() throws Exception {
PrototypeBean bean = beanFactory.getObject();
try {
// Использовать bean
bean.doWork();
} finally {
// Явно очистить
bean.cleanup();
}
}
}
@Component
@Scope("prototype")
public class PrototypeBean {
public void doWork() { }
public void cleanup() {
System.out.println("Manual cleanup");
}
}
Вариант 3: DestroyableBean Interface
@Component
@Scope("prototype")
public class ManagedPrototypeBean {
@PostConstruct
public void init() {
System.out.println("Init");
}
// Используй вместо @PreDestroy
public void close() {
System.out.println("Close");
}
}
// Использование с try-with-resources или явное закрытие
public class Service {
@Autowired
private ObjectFactory<ManagedPrototypeBean> beanFactory;
public void doSomething() {
ManagedPrototypeBean bean = beanFactory.getObject();
try {
// work
} finally {
bean.close(); // Явный вызов
}
}
}
Вариант 4: Bean Post Processor
@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof PrototypeBean) {
// Зарегистрировать для track'инга
registerForDestruction((PrototypeBean) bean);
}
return bean;
}
private void registerForDestruction(PrototypeBean bean) {
// Использовать например в Map для отслеживания
}
}
Вариант 5: Правильный Scope
Лучший подход: использовать правильный scope!
// Если нужно управлять жизненным циклом:
// Используй Singleton вместо Prototype
@Component
@Scope("singleton")
public class MyService {
@PostConstruct
public void init() {
System.out.println("Init");
}
@PreDestroy
public void destroy() {
System.out.println("Destroy"); // Выполнится
}
}
// Если нужны изолированные экземпляры:
// Используй Request или Session scope
@Component
@Scope("request")
public class RequestScopedService {
@PostConstruct
public void init() {
System.out.println("Init per request");
}
@PreDestroy
public void destroy() {
System.out.println("Destroy per request"); // Выполнится
}
}
Ответ на собеседовании
Вопрос: Выполнится ли метод с аннотацией PreDestroy у Bean с Prototype Scope в Spring?
Ответ:
Нет, метод с @PreDestroy не выполнится. Это потому, что:
- Spring управляет созданием prototype bean'ов (@PostConstruct выполнится)
- Но Spring не управляет их уничтожением
- После создания и инициализации Spring "отпускает" bean приложению
- Приложение полностью ответственно за очистку ресурсов
- Поэтому @PreDestroy не срабатывает
Этом отличается от singleton, request и session scope'ов, где Spring управляет полным жизненным циклом.
Если нужна гарантированная очистка, следует использовать singleton, request/session scope, или явно вызывать cleanup методы.
Заключение
- @PreDestroy НЕ выполнится для Prototype scope ✗
- @PostConstruct выполнится ✓ (Spring всё ещё управляет созданием)
- Spring отпускает управление после инициализации
- Используй правильный scope для твоего случая использования
- Если нужна очистка, используй Singleton или Request/Session scope
- Или явно управляй жизненным циклом через ObjectFactory