Будет ли prototype бин, созданный на запрос, виден в другом потоке при параллельном выполнении через ExecutorService в Spring
Что проверяют
Понимаете ли вы, что scope в Spring отвечает за то, сколько экземпляров создаст контейнер, а не за видимость объекта между потоками. Следом почти всегда спрашивают, что будет с prototype, внедрённым в singleton, и как получить request-бин внутри пула потоков.
Как отвечать
Начните с разделения понятий:
- prototype: контейнер создаёт новый экземпляр при каждом запросе к нему, то есть при каждом
getBeanили внедрении. К потокам это отношения не имеет, а уничтожение prototype Spring не отслеживает,@PreDestroyне вызовется; - request: бин живёт в рамках HTTP-запроса и привязан к потоку через
RequestContextHolder, внутри этоThreadLocal; - видимость между потоками: обычные правила Java Memory Model. Prototype-бин остаётся обычным объектом, и если передать ссылку в задачу, другой поток её увидит.
ExecutorService.submitгарантирует happens-before: всё, что записано до отправки задачи, видно внутри неё.
Отсюда ответ. Если вы получили prototype в одном потоке и передали его в Runnable, второй поток работает с тем же экземпляром, и о потокобезопасности его состояния заботитесь вы. Если нужен свой экземпляр на задачу, запрашивайте его внутри задачи: ObjectProvider<MyBean>.getObject(), метод с @Lookup или ApplicationContext.getBean. Каждый вызов вернёт новый объект.
Если же под «бином на запрос» имеется в виду request scope, то в потоке из пула он недоступен: прокси бросит IllegalStateException про отсутствие thread-bound request. Нужные данные извлекают заранее и передают в задачу явно, а контекст переносят через TaskDecorator у ThreadPoolTaskExecutor.
Где спотыкаются
Считают, что prototype означает «свой экземпляр на поток». Spring про потоки ничего не знает, для этого есть отдельный SimpleThreadScope, и по умолчанию он не зарегистрирован.
Внедряют prototype полем в singleton и ждут нового объекта на каждый вызов. Внедрение происходит один раз, и все потоки делят один экземпляр.
Путают prototype и request scope. Даже перенеся атрибуты запроса в другой поток, легко получить обращение к запросу, который уже завершился.