В приложении Spring Batch мы реализовали область действия элемента .
Фон
У нас есть множество @Service
компонентов, которые вычисляют что-то на основе текущего элемента пакета. Многие из них нуждаются в одном и том же рабочем процессе:
- Определите соответствующие детали элемента.
- Инициализация вещей, основанных на предмете.
- Для каждой части предмета что-то вычислить (используя вещи).
Мы переместили рабочий процесс в метод шаблона базового класса, поэтому подклассы реализуют только findItemParts(Item)
(выполнение 1 и 2) и computeSomething(ItemPart)
(выполнение 3). Таким образом, они стали с состоянием (материал, инициализированный в findItemParts
, необходим в computeSomething
), и это состояние должно быть очищено перед следующим элементом.
Некоторые из этих сервисов также включают внедренные бины Spring, которые также получены из текущего элемента и должны быть впоследствии удалены.
Дизайн
Мы реализовали AbstractScopeRegisteringItemProcessor
, который регистрирует элемент и позволяет подклассам регистрировать производные бины. В конце метода process
он удаляет элемент из контекста своей области, а производные бины используют DefaultSingletonBeanRegistry.destroySingleton
.
Как это получилось
Работает, но имеет следующие проблемы:
- Нам не удалось очистить производные бины без регистрации (только на основании их
@Scope
). Конкретный процессор должен их создать и зарегистрировать.
AbstractScopeRegisteringItemProcessor
было бы лучше использовать композицию и динамически реализовывать все интерфейсы базового процессора. Но тогда результирующий компонент @StepScope
является прокси для объявленного возвращаемого типа (т. Е. AbstractScopeRegisteringItemProcessor
или ItemProcessor
) без необходимых интерфейсов обратного вызова.
* * РЕДАКТИРОВАТЬ тысячу сорок-девять
С помощью решения @Eliot Sykes и общего кода плюс регистрация BeanDefinition @ Cheetah я смог избавиться от регистрации в виде синглтон-бинов. Вместо этого ItemScopeContext
(хранилище, используемое как процессором, так и реализацией Scope
; Java-конфигурируется статическим методом @Bean
) реализует BeanDefinitionRegistryPostProcessor
. Он регистрирует FactoryBean
, чей getObject()
возвращает текущий элемент или выдает исключение, если его нет. Теперь @Component
, помеченный @Scope(scopeName = "Item", proxyMode = ScopedProxyMode.TARGET_CLASS)
, может просто внедрить элемент, и его не нужно регистрировать для очистки конца области действия.
Итак, в конце концов, это сработало хорошо.