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

Уничтожается ли prototype бин сразу после инъекции

2.4 Senior🔥 101 комментариев
#Spring Boot и Spring Data#Spring Framework

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Жизненный цикл 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

  1. Stateful объекты — когда каждый клиент нужна собственная копия с собственным состоянием
  2. Request-scoped данные — информация, специфичная для текущей операции
  3. Тяжелые объекты — когда нежелательно кэшировать между запросами
  4. Avoid memory leaks — если объект потребляет ресурсы и должен быть очищен после использования

Заключение

Prototype бины не уничтожаются сразу после инъекции и не управляются контейнером Spring в отношении очистки ресурсов. Разработчик отвечает за правильное управление их жизненным циклом. Используйте ObjectProvider для получения новых экземпляров в singleton бинах и не забывайте об особенностях Garbage Collection при работе с prototype scope'ом.

Уничтожается ли prototype бин сразу после инъекции | PrepBro