Я пытаюсь использовать события жизненного цикла Spring Data MongoDB для вычисления поля «после сохранения» на основе идентификатора, который был сгенерирован MongoDB при вставке. Справочная документация говорит следующее о другом событии, 'перед сохранением':
Чтобы перехватить объект перед его поступлением в базу данных, вы можете зарегистрировать подкласс org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener, который переопределяет метод onBeforeSave.Когда событие отправляется, ваш слушатель вызывается и передает объект домена и преобразованный com.mongodb.Document.В следующем примере показано, как это сделать:
Затем они предоставляют пример.
Я сделал нечто подобное для 'после сохранения':
public class MyMongoDbLifecycleListener extends AbstractMongoEventListener<MyModel> {
@Override
public void onAfterSave(AfterSaveEvent<MyModel> event) {
super.onAfterSave(event);
MyModel model = event.getSource();
model.computeValueFromTheAssignedId();
}
}
Этоработает в тестах, но в предпроизводственном тестировании мы натолкнулись на проблему: иногда (на самом деле, довольно часто) значение не вычисляется заранее до того момента, когда объект возвращается из метода Repository
save()
.
Проблема в том, что события приложения обрабатываются в нашем приложении асинхронно.В нашей конфигурации есть следующее:
<bean id="simpleAsyncTaskExecutor" class="org.springframework.core.task.SimpleAsyncTaskExecutor">
...
</bean>
<bean id="applicationEventMulticaster" class="org.springframework.context.event.SimpleApplicationEventMulticaster">
<property name="taskExecutor" ref="simpleAsyncTaskExecutor" />
...
</bean>
В SimpleApplicationEventMulticaster
есть следующий код, который фактически выполняет многоадресную рассылку:
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
Итак, многоадресная рассылка контекста нашего приложения все генерирует события асинхронно и вызывает слушателей в потоке, который отличается от потока, в котором мы вызываем save()
, поэтому у нас нет гарантии увидеть эффект слушателя после сохранения.
I 'Я в замешательстве.Формулировка документации («Перехватить объект перед ...») ясно указывает, что это происходит строго перед преобразованием / сохранением / чем-либо еще.Но простое (и естественное) изменение конфигурации нарушает эти гарантии.Я подозреваю, что я делаю что-то не так, но как мне сделать это правильно?
Единственная идея, которая у меня сейчас есть, - написать расширение мультикастера, которое бы специально обрабатывало все события, связанные с MongoDB, синхронно.Это должно работать, но должно ли быть так легко нарушить поведение системы?