Ответ
Spring контейнер по-разному управляет завершением жизненного цикла для бинов Singleton и Prototype.
Singleton Scope
- Бин создается один раз при инициализации контекста.
- Spring полностью управляет его жизненным циклом.
- Метод уничтожения (аннотация
@PreDestroyилиdestroy-method) вызывается автоматически при закрытии контекста приложения (ApplicationContext.close()).
Prototype Scope
- Новый экземпляр создается при каждом запросе (
getBean()или инъекции). - Spring передает созданный объект клиенту и прекращает управление его жизненным циклом.
- Методы уничтожения НЕ вызываются автоматически. Ответственность за очистку ресурсов лежит на клиентском коде.
Пример и сравнение:
@Component
@Scope("singleton")
public class SingletonBean {
@PreDestroy
public void cleanup() {
System.out.println("SingletonBean: destroy method called");
}
}
@Component
@Scope("prototype")
public class PrototypeBean {
@PreDestroy
public void cleanup() {
System.out.println("PrototypeBean: destroy method called"); // НИКОГДА не вызовется Spring'ом
}
}
Сценарий выполнения:
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
SingletonBean sb1 = ctx.getBean(SingletonBean.class);
SingletonBean sb2 = ctx.getBean(SingletonBean.class); // sb1 == sb2
PrototypeBean pb1 = ctx.getBean(PrototypeBean.class);
PrototypeBean pb2 = ctx.getBean(PrototypeBean.class); // pb1 != pb2
ctx.close(); // Вызовет cleanup() только для SingletonBean
Рекомендация для Prototype бинов: Используйте шаблон disposable bean или явно вызывайте метод очистки в клиентском коде:
public class ClientService {
@Autowired
private ObjectFactory<PrototypeBean> prototypeBeanFactory;
public void process() {
PrototypeBean bean = prototypeBeanFactory.getObject();
try {
// работа с бином
} finally {
// Явный вызов cleanup
if (bean instanceof DisposableBean) {
((DisposableBean) bean).destroy();
}
}
}
} Ответ 18+ 🔞
А, слушай, вот эта вся хуйня с бинами в Spring — она, блядь, такая хитрая, что мозг можно сломать. Особенно когда дело доходит до того, как они помирают, эти твои бины. Так вот, пиздец, запомни раз и навсегда:
Singleton — это как царь и бог в своём контексте. Создали его один раз, когда приложение запустилось, и он там сидит, блядь, как пизда с ушами, до самого конца. Spring за ним, сука, присматривает как за родным: накормит, напоит, и когда придёт время закрывать лавочку (ApplicationContext.close()), он ему вежливо так скажет: «Ну всё, дружок, пора на тот свет» — и вызовет метод уничтожения. Честно, по-пацански.
А вот Prototype — это, блядь, совсем другая история, ёпта! Это как одноразовые стаканчики. Захотел — взял новый (getBean()), попил, выкинул. Spring тебе этот стаканчик вручил и пошёл нахуй. Он больше про него нихуя не знает и не хочет знать. Управлять его смертью? Да хуй там! Твой @PreDestroy он проигнорирует с таким видом, будто это не его собака бегала. Он его даже в списке покойников не отметит, вот те крест.
Смотри, вот тебе наглядный пиздец, прости, пример:
@Component
@Scope("singleton")
public class SingletonBean {
@PreDestroy
public void cleanup() {
System.out.println("SingletonBean: destroy method called"); // ЭТО ВЫЗОВЕТСЯ, БЛЯДЬ!
}
}
@Component
@Scope("prototype")
public class PrototypeBean {
@PreDestroy
public void cleanup() {
System.out.println("PrototypeBean: destroy method called"); // А ЭТО — НИКОГДА, ХУЙ ТЕБЕ, А НЕ ВЫЗОВ!
}
}
И вот как это всё, сука, работает:
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
SingletonBean sb1 = ctx.getBean(SingletonBean.class);
SingletonBean sb2 = ctx.getBean(SingletonBean.class); // sb1 == sb2, один и тот же мудак
PrototypeBean pb1 = ctx.getBean(PrototypeBean.class);
PrototypeBean pb2 = ctx.getBean(PrototypeBean.class); // pb1 != pb2, каждый раз новый мудак
ctx.close(); // И вот тут Spring вызовет cleanup() ТОЛЬКО для SingletonBean. Про Prototype он забудет, как про вчерашний запой.
Так что, если ты работаешь с Prototype, то ты, дружок, сам себе злобный буратино. Сам создал — сам и прибивай. Вот тебе, блядь, совет, как не обосраться с утечками памяти:
public class ClientService {
@Autowired
private ObjectFactory<PrototypeBean> prototypeBeanFactory; // Хитрая жопа, даёт фабрику
public void process() {
PrototypeBean bean = prototypeBeanFactory.getObject(); // Достал новый экземпляр
try {
// делай тут что хочешь с этим бедным бином
} finally {
// А вот тут, сука, ОБЯЗАТЕЛЬНО прибери за собой!
// Сам вызови его destroy-метод, если он реализует DisposableBean
if (bean instanceof DisposableBean) {
((DisposableBean) bean).destroy(); // Вот так, по-взрослому
}
}
}
}
А иначе получишь овердохуища мусора, и приложение твоё накроется медным тазом. Не говори потом, что я не предупреждал, ёпта!