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

Выполнится ли метод с аннотацией 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              Приложение
  управляет      управляет            управляет

Причины дизайна

  1. Управление памятью: Prototype bean'ов может быть много, приложение должно управлять их очисткой
  2. Множественные экземпляры: У приложения есть ссылка на bean, Spring не знает, когда его удалять
  3. Ответственность: Если создал объект, ты ответственен за его очистку

Как работает с другими 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УправлениеCreateInitDestroy
SingletonSpringSpringSpringSpring ✓
PrototypeПриложениеSpringSpring
RequestSpringSpringSpringSpring ✓
SessionSpringSpringSpringSpring ✓
WebSocketSpringSpringSpringSpring ✓
ApplicationSpringSpringSpringSpring ✓

Как обойти эту проблему

Вариант 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 не выполнится. Это потому, что:

  1. Spring управляет созданием prototype bean'ов (@PostConstruct выполнится)
  2. Но Spring не управляет их уничтожением
  3. После создания и инициализации Spring "отпускает" bean приложению
  4. Приложение полностью ответственно за очистку ресурсов
  5. Поэтому @PreDestroy не срабатывает

Этом отличается от singleton, request и session scope'ов, где Spring управляет полным жизненным циклом.

Если нужна гарантированная очистка, следует использовать singleton, request/session scope, или явно вызывать cleanup методы.

Заключение

  • @PreDestroy НЕ выполнится для Prototype scope ✗
  • @PostConstruct выполнится ✓ (Spring всё ещё управляет созданием)
  • Spring отпускает управление после инициализации
  • Используй правильный scope для твоего случая использования
  • Если нужна очистка, используй Singleton или Request/Session scope
  • Или явно управляй жизненным циклом через ObjectFactory
Выполнится ли метод с аннотацией PreDestroy у Bean с Prototype Scope в Spring? | PrepBro