我正在尝试使用Spring Data MongoDB生命周期事件基于插入时MongoDB生成的ID计算“保存后”字段。 Reference documentation关于另一个事件,“保存前”说以下话:
要在对象进入数据库之前对其进行拦截,可以注册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()
的线程不同的线程中调用侦听器,因此我们无法保证在保存后会看到侦听器的效果。我糊涂了。文档中的措辞(“在...之前拦截对象”)清楚地表明,这种情况严格在转换/保存/任何操作之前发生。但是简单(自然)的配置更改会破坏这些保证。我怀疑我做错了什么,但是我应该如何正确做呢?
我现在唯一的想法是编写一个多播器的扩展,该扩展将专门同步处理所有与MongoDB相关的事件。那应该起作用,但是破坏系统行为是否应该那么容易?
最佳答案
这是SimpleApplicationEventMulticaster
扩展名,它允许实现“针对spring-data-mongodb事件的同步处理”策略:
public class SystemEventsAreSyncronousMulticaster extends SimpleApplicationEventMulticaster {
@Override
public void multicastEvent(ApplicationEvent event, ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor == null || eventMustBeProcessedSynchronously(event)) {
invokeListener(listener, event);
} else {
executor.execute(() -> invokeListener(listener, event));
}
}
}
private boolean eventMustBeProcessedSynchronously(ApplicationEvent event) {
return event instanceof MongoMappingEvent;
}
private ResolvableType resolveDefaultEventType(ApplicationEvent event) {
return ResolvableType.forInstance(event);
}
}
关于java - Spring Data MongoDB生命周期事件:是否可以始终同步处理它们?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/56391260/