Уничтожается ли prototype бин сразу после инъекции
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Жизненный цикл Prototype бинов в Spring
Нет, prototype бин НЕ уничтожается сразу после инъекции. Это ключевое отличие scope'а prototype от singleton.
Что такое Scope Bean
Scope — область видимости и время жизни бина в контексте Spring. Spring предоставляет несколько встроенных scope'ов:
- singleton (по умолчанию) — один экземпляр на весь контекст приложения
- prototype — новый экземпляр при каждом запросе
- request — один экземпляр на HTTP запрос (web-приложения)
- session — один экземпляр на HTTP сессию (web-приложения)
- global session — один экземпляр на глобальную сессию (portlets)
- application — один экземпляр на жизненный цикл ServletContext
Жизненный цикл Prototype Bean
Prototype бины создаются при каждом запросе:
@Component
@Scope("prototype")
public class PrototypeService {
public PrototypeService() {
System.out.println("PrototypeService создан: " + this.hashCode());
}
@PostConstruct
public void init() {
System.out.println("@PostConstruct вызван");
}
@PreDestroy
public void destroy() {
System.out.println("@PreDestroy вызван");
}
}
@Component
public class ConsumerService {
@Autowired
private PrototypeService prototypeService;
public void doSomething() {
// Используем бин
}
}
Выход при запуске:
PrototypeService создан: 12345
@PostConstruct вызван
Важно: @PreDestroy НЕ вызовется! Spring не управляет жизненным циклом prototype бинов после их создания.
Различия между Singleton и Prototype
Singleton:
@Component
@Scope("singleton") // По умолчанию
public class SingletonService {
public SingletonService() {
System.out.println("SingletonService создан");
}
@PostConstruct
public void init() { System.out.println("init"); }
@PreDestroy
public void destroy() { System.out.println("destroy"); }
}
- Создаётся один раз при инициализации контекста
@PostConstructвызывается один раз@PreDestroyвызывается при завершении контекста- Используется одна и та же ссылка повсюду
Prototype:
@Component
@Scope("prototype")
public class PrototypeService {
@PostConstruct
public void init() { System.out.println("init"); }
@PreDestroy
public void destroy() { System.out.println("destroy"); }
}
@Service
public class MyService {
@Autowired
private PrototypeService service1;
@Autowired
private PrototypeService service2; // Это будет ДРУГОЙ объект!
public void test() {
System.out.println(service1 == service2); // false
}
}
- Создаётся НОВЫЙ экземпляр при каждой инъекции
@PostConstructвызывается при каждом создании@PreDestroyНЕ вызывается (Spring не управляет уничтожением)- Разные ссылки на разные объекты
Важные нюансы с Prototype
1. Garbage Collection отвечает за уничтожение
Spring не управляет жизненным циклом prototype бинов. Когда на объект нет ссылок, его удаляет Garbage Collector. До этого @PreDestroy не вызовется.
2. Injection в Singleton бин (потенциальная проблема)
@Component
public class SingletonService {
@Autowired
private PrototypeService prototypeService;
public void doWork() {
// Это будет один и тот же prototype объект!
// Spring инъецировал его ДО инициализации singleton
// Потом prototype никогда не меняется
}
}
Решение — использовать ObjectProvider или ObjectFactory:
@Component
public class SingletonService {
@Autowired
private ObjectProvider<PrototypeService> prototypeProvider;
public void doWork() {
// Каждый раз получаем НОВЫЙ объект
PrototypeService service = prototypeProvider.getObject();
service.doSomething();
}
}
3. Использование прокси
@Component
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class PrototypeService {
// Spring создаст прокси, через который всегда
// будет получаться новый экземпляр
}
Практический пример: обработка request-data
@Component
@Scope("prototype")
public class RequestProcessor {
private String requestId;
private LocalDateTime createdAt;
@PostConstruct
public void init() {
this.requestId = UUID.randomUUID().toString();
this.createdAt = LocalDateTime.now();
System.out.println("Processor создан для запроса " + requestId);
}
public void process(String data) {
System.out.println("Обработка " + data);
}
}
@Service
public class WebService {
@Autowired
private ObjectProvider<RequestProcessor> processorProvider;
public void handleRequest(String data) {
RequestProcessor processor = processorProvider.getObject();
processor.process(data);
// После выхода из метода processsor будет доступен для GC
}
}
Когда использовать Prototype
- Stateful объекты — когда каждый клиент нужна собственная копия с собственным состоянием
- Request-scoped данные — информация, специфичная для текущей операции
- Тяжелые объекты — когда нежелательно кэшировать между запросами
- Avoid memory leaks — если объект потребляет ресурсы и должен быть очищен после использования
Заключение
Prototype бины не уничтожаются сразу после инъекции и не управляются контейнером Spring в отношении очистки ресурсов. Разработчик отвечает за правильное управление их жизненным циклом. Используйте ObjectProvider для получения новых экземпляров в singleton бинах и не забывайте об особенностях Garbage Collection при работе с prototype scope'ом.