Когда создается бин с скоупом prototype в Spring
Что проверяют
Знаете ли вы, что prototype это не «много синглтонов», а другой момент создания и другой жизненный цикл. Практический смысл вопроса всплывает сразу следом: что произойдёт, если prototype внедрить в singleton.
Как отвечать
Prototype создаётся в момент запроса, а не при старте контекста. Запрос это getBean(), обращение через ObjectProvider, вызов @Bean-метода в конфигурации или инъекция в другой бин на этапе его сборки. Singleton по умолчанию создаётся жадно при подъёме контекста, если не помечен @Lazy. Prototype не создаётся вовсе, пока его не попросят.
Ключевые следствия:
- инъекция в singleton даёт один экземпляр: поле заполняется однократно, когда собирается сам singleton, и новых объектов дальше не появляется. Это самая частая ошибка с prototype;
- как получать новый экземпляр:
ObjectProvider<T>сgetObject()(сейчас основной способ), метод-фабрика с@Lookup, либо скоуп-прокси через@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS). Прямое обращение кApplicationContextработает, но привязывает код к контейнеру; - контейнер не хранит ссылку:
@PostConstructиBeanPostProcessorотрабатывают, а@PreDestroyиDisposableBeanдля prototype не вызываются никогда. Освобождение ресурсов остаётся на вас или наDestructionAwareBeanPostProcessor; - циклическая зависимость между prototype-бинами не разрешается: контейнеру нечего подставить, получите
BeanCurrentlyInCreationException.
Когда скоуп действительно нужен: объект с состоянием на одну операцию, накопитель, билдер, парсер с внутренним курсором. Для веб-сценариев чаще подходят request и session, а не prototype.
Где спотыкаются
Внедряют prototype в singleton обычным полем и удивляются общему состоянию между запросами. Скоуп указан, но сработал он ровно один раз.
Ждут вызова @PreDestroy и оставляют утечку соединений или потоков. Контейнер забывает про такой бин сразу после того, как отдал его.
Считают prototype решением проблем многопоточности. Он лишь не переиспользует объект. Если несколько потоков получили один экземпляр через общую ссылку, гонки никуда не делись.