← Назад к вопросам
Контролирует ли Spring Scope Prototype
2.2 Middle🔥 171 комментариев
#Spring Framework
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Spring Bean Scope Prototype: Как Spring контролирует время жизни
Это отличный вопрос, раскрывающий глубокое понимание Spring контейнера. Ответ неочевидный: Spring НЕ контролирует полный жизненный цикл Prototype бина, только его создание.
Основные Spring Scopes
1. Singleton (по умолчанию)
@Bean // или @Service, @Component по умолчанию
public UserService userService() {
return new UserService();
}
// Spring контролирует:
// - Создание (один раз при старте)
// - Инициализацию (вызов @PostConstruct)
// - Кэширование (тот же экземпляр всегда)
// - Уничтожение (вызов @PreDestroy при shutdown)
// Время жизни: От создания до shutdown приложения
2. Prototype (ключевой момент)
@Component
@Scope("prototype")
public class RequestContext {
@PostConstruct
public void init() {
System.out.println("RequestContext инициализирован");
}
@PreDestroy
public void destroy() {
System.out.println("RequestContext уничтожен");
}
}
// Spring контролирует:
// ✅ Создание (каждый раз новый экземпляр)
// ✅ Инициализацию (вызов @PostConstruct)
// ❌ Уничтожение (NOT контролирует @PreDestroy!)
// ❌ Кэширование (не кэширует)
// Время жизни: От создания до... ??? (Spring не контролирует)
Ключевое различие: Singleton vs Prototype
// SINGLETON
@Component
@Scope("singleton") // или просто @Component (default)
public class SingletonService {
@PostConstruct
public void init() {
System.out.println("SingletonService создан");
}
@PreDestroy
public void cleanup() {
System.out.println("SingletonService удалён");
}
}
// PROTOTYPE
@Component
@Scope("prototype")
public class PrototypeService {
@PostConstruct
public void init() {
System.out.println("PrototypeService создан");
}
@PreDestroy
public void cleanup() {
System.out.println("PrototypeService удалён"); // ❌ НЕ будет вызван!
}
}
// Тест
@SpringBootTest
public class ScopeTest {
@Autowired
private ApplicationContext context;
@Test
public void testSingletonLifecycle() {
SingletonService s1 = context.getBean(SingletonService.class);
SingletonService s2 = context.getBean(SingletonService.class);
// Output: "SingletonService создан" (один раз!)
System.out.println(s1 == s2); // true (один и тот же объект)
// При shutdown приложения:
// Output: "SingletonService удалён"
}
@Test
public void testPrototypeLifecycle() {
PrototypeService p1 = context.getBean(PrototypeService.class);
PrototypeService p2 = context.getBean(PrototypeService.class);
// Output:
// "PrototypeService создан"
// "PrototypeService создан" (два раза!)
System.out.println(p1 == p2); // false (разные объекты)
// При shutdown приложения:
// Output: (ничего - @PreDestroy не вызывается!)
// p1 и p2 остаются в памяти (memory leak потенциальный)
}
}
Почему Spring НЕ контролирует Prototype @PreDestroy?
Это по дизайну:
// Spring выдаёт вам Prototype бин
PrototypeService prototype1 = context.getBean(PrototypeService.class);
PrototypeService prototype2 = context.getBean(PrototypeService.class);
PrototypeService prototype3 = context.getBean(PrototypeService.class);
// ... миллиард других бинов
// Как Spring узнает, когда удалить прототип?
// Может быть, вы ещё используете prototype1?
// Может быть, prototype2 передали в другой объект?
// Может быть, prototype3 хранится в какой-то коллекции?
// Spring не может следить за всеми ссылками
// Это работа Garbage Collector!
// Когда на бин нет ссылок → GC удаляет объект
// Если бин имеет @PreDestroy, JVM вызовет его только при финализации
// Но это ненадёжно (финализация может быть отложена или пропущена)
Явное управление жизненным циклом Prototype
Если нужно гарантировать cleanup, используйте это:
1. ObjectFactory
@Service
public class RequestProcessor {
@Autowired
private ObjectFactory<RequestContext> contextFactory;
public void processRequest(String data) {
// Создаём новый бин для каждого запроса
RequestContext context = contextFactory.getObject();
try {
context.process(data);
} finally {
// Явно вызываем cleanup
context.cleanup();
}
}
}
// RequestContext
@Component
@Scope("prototype")
public class RequestContext {
@PostConstruct
public void init() {
System.out.println("Context создан");
}
public void process(String data) {
System.out.println("Обработка: " + data);
}
public void cleanup() {
System.out.println("Cleanup вызван явно");
}
}
2. ObjectProvider (более гибкий)
@Service
public class DataProcessor {
@Autowired
private ObjectProvider<ProcessingContext> contextProvider;
public void process(List<String> items) {
for (String item : items) {
// Получаем новый контекст для каждого элемента
ProcessingContext context = contextProvider.getObject();
try {
doProcess(item, context);
} finally {
if (context instanceof AutoCloseable) {
((AutoCloseable) context).close();
}
}
}
}
private void doProcess(String item, ProcessingContext context) {
context.handle(item);
}
}
3. DisposableBean интерфейс
@Component
@Scope("prototype")
public class PrototypeResource implements DisposableBean {
private Connection connection;
@PostConstruct
public void init() {
this.connection = DatabaseUtil.getConnection();
System.out.println("Соединение установлено");
}
@Override
public void destroy() throws Exception {
// Когда бин удаляется, закрываем ресурсы
if (connection != null) {
connection.close();
System.out.println("Соединение закрыто");
}
}
}
Другие Scopes в Spring
// 1. REQUEST (для веб приложений)
@Component
@Scope("request")
public class RequestData {
// Новый экземпляр для каждого HTTP запроса
// Spring управляет жизненным циклом
}
// 2. SESSION (для веб приложений)
@Component
@Scope("session")
public class UserSession {
// Один экземпляр на пользователя/сессию
// Spring управляет жизненным циклом
}
// 3. APPLICATION (для веб приложений)
@Component
@Scope("application")
public class ApplicationConfig {
// Один экземпляр на приложение (как singleton)
// Но для ServletContext
}
// 4. WEBSOCKET (для WebSocket приложений)
@Component
@Scope("websocket")
public class WebSocketMessage {
// Один экземпляр на WebSocket сессию
}
Пример: Потенциальная проблема с Prototype
// ❌ ПРОБЛЕМА: Singleton содержит ссылку на Prototype
@Component
public class SingletonService {
@Autowired
private PrototypeService prototype;
public void doWork() {
// ❌ prototype всегда один и тот же объект!
// Scope="prototype" игнорируется
prototype.process();
}
}
// ✅ РЕШЕНИЕ: Используйте ObjectFactory
@Component
public class SingletonService {
@Autowired
private ObjectFactory<PrototypeService> prototypeFactory;
public void doWork() {
// ✅ Каждый раз новый PrototypeService
PrototypeService proto = prototypeFactory.getObject();
proto.process();
}
}
// ✅ АЛЬТЕРНАТИВА: Используйте @Lookup
@Component
public class SingletonService {
@Lookup
protected PrototypeService getPrototype() {
return null; // Spring переопределит этот метод
}
public void doWork() {
// ✅ Каждый раз новый PrototypeService
PrototypeService proto = getPrototype();
proto.process();
}
}
Таблица управления жизненным циклом
| Scope | Создание | @PostConstruct | @PreDestroy | Кэширование |
|---|---|---|---|---|
| Singleton | Один раз (старт) | ✅ Вызывается | ✅ Вызывается | ✅ Да |
| Prototype | Каждый раз | ✅ Вызывается | ❌ НЕ вызывается | ❌ Нет |
| Request | На запрос | ✅ Вызывается | ✅ Вызывается | Только в запросе |
| Session | На сессию | ✅ Вызывается | ✅ Вызывается | Только в сессии |
Вывод
Ответ на вопрос: "Контролирует ли Spring Scope Prototype?"
Частично:
✅ Spring контролирует:
- Создание новых экземпляров
- Вызов @PostConstruct
- Внедрение зависимостей
❌ Spring НЕ контролирует:
- Удаление объектов (@PreDestroy не вызывается)
- Время жизни (это работа GC)
- Cleanup ресурсов
Поэтому с Prototype бинами нужно быть осторожным:
- Используйте ObjectFactory для явного управления жизненным циклом
- Реализуйте DisposableBean для очистки ресурсов
- Помните про потенциальные memory leaks
Rule of thumb: Используйте Prototype редко. В большинстве случаев достаточно Singleton с внедрёнными зависимостями.