我正在尝试使用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();
    }
}


这在测试中有效,但是在生产前测试中,我们偶然发现了一个问题:有时(实际上,很多时候)该值并未从Repositorysave()方法返回对象时被预先计算。

问题在于应用程序事件是在我们的应用程序中异步处理的。我们的配置中包含以下内容:

<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/

10-11 15:18
查看更多