【发布时间】:2019-05-31 08:16:32
【问题描述】:
我正在尝试使用 Spring Data MongoDB 生命周期事件根据 MongoDB 在插入时生成的 ID 计算“保存后”字段。 Reference documentation 对不同的事件“保存前”说如下:
要在对象进入数据库之前对其进行拦截,您可以注册一个覆盖 onBeforeSave 方法的 org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener 的子类。调度事件时,将调用您的侦听器并传递域对象和转换后的 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()的线程不同的线程中调用侦听器,因此我们无法保证看到a的效果保存后的监听器。
我很困惑。文档措辞('在...之前拦截一个对象')清楚地表明这发生在严格地在转换/保存/其他之前。但是一个简单(自然)的配置更改会破坏这些保证。我怀疑我做错了什么,但我应该如何正确地做呢?
我现在唯一的想法是编写一个多播器的扩展,它将专门同步处理所有与 MongoDB 相关的事件。这应该可以,但是破坏系统行为应该这么容易吗?
【问题讨论】:
-
我给出了一个大胆的猜测,但如果你在事务中,对象可能还没有存储在数据库中,并且事件在调用 save 之后但在实际提交之前传播。
-
谢谢@Mihai,但我们不使用交易。而且事件实际上是在另一个线程中处理的,没有应用任何外部同步,所以没有办法保证顺序(我通过代码调试了这个)。
标签: java spring spring-data spring-data-mongodb