生命周期事件

MongoDB 映射框架包含多个 org.springframework.context.ApplicationEvent 事件,您的应用程序可以通过在 ApplicationContext 中注册特殊 Bean 来响应这些事件。基于 Spring 的 ApplicationContext 事件基础设施,使其他产品(如 Spring Integration)能够轻松接收这些事件,因为它们是基于 Spring 的应用程序中众所周知的事件机制。

实体生命周期事件可能很昂贵,您可能会注意到在加载大型结果集时性能配置文件发生了变化。您可以在 模板 API 上禁用生命周期事件。

要拦截对象在进入转换过程(将您的域对象转换为 org.bson.Document)之前,您可以注册一个 AbstractMongoEventListener 的子类,该子类覆盖 onBeforeConvert 方法。当事件被分派时,您的监听器会被调用并传递域对象,然后它进入转换器。以下示例展示了如何做到这一点

public class BeforeConvertListener extends AbstractMongoEventListener<Person> {
  @Override
  public void onBeforeConvert(BeforeConvertEvent<Person> event) {
    ... does some auditing manipulation, set timestamps, whatever ...
  }
}

为了在对象进入数据库之前拦截它,您可以注册一个org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener的子类,并覆盖onBeforeSave方法。当事件被分发时,您的监听器会被调用,并传递领域对象和转换后的com.mongodb.Document。以下示例展示了如何做到这一点。

public class BeforeSaveListener extends AbstractMongoEventListener<Person> {
  @Override
  public void onBeforeSave(BeforeSaveEvent<Person> event) {
    … change values, delete them, whatever …
  }
}

在您的 Spring ApplicationContext 中声明这些 Bean 会导致它们在每次事件被分发时被调用。

AbstractMappingEventListener 上的回调
  • onBeforeConvert:在MongoTemplateinsertinsertListsave操作中,在对象被MongoConverter转换为Document之前被调用。

  • onBeforeSave:在MongoTemplateinsertinsertListsave操作中,Document插入或保存到数据库之前被调用。

  • onAfterSave:在MongoTemplateinsertinsertListsave操作中,Document插入或保存到数据库之后被调用。

  • onAfterLoad:在MongoTemplatefindfindAndRemovefindOnegetCollection方法中,在Document从数据库中检索出来之后被调用。

  • onAfterConvert:在MongoTemplatefindfindAndRemovefindOnegetCollection方法中,在从数据库中检索出来的Document被转换为POJO之后被调用。

生命周期事件只针对根级别类型发出。用作文档根内属性的复杂类型不会发布事件,除非它们是使用@DBRef注释的文档引用。
生命周期事件依赖于ApplicationEventMulticaster,在SimpleApplicationEventMulticaster的情况下,它可以配置TaskExecutor,因此不保证何时处理事件。

实体回调

Spring Data 基础设施提供了在调用某些方法之前和之后修改实体的钩子。这些所谓的 EntityCallback 实例提供了一种方便的方式,以回调的方式检查和修改实体。
EntityCallback 看起来很像一个专门的 ApplicationListener。一些 Spring Data 模块发布特定于存储的事件(例如 BeforeSaveEvent),这些事件允许修改给定的实体。在某些情况下,例如在使用不可变类型时,这些事件可能会导致问题。此外,事件发布依赖于 ApplicationEventMulticaster。如果使用异步 TaskExecutor 配置它,可能会导致不可预测的结果,因为事件处理可能会被分叉到一个线程中。

实体回调提供了与同步和反应式 API 的集成点,以保证在处理链中定义明确的检查点处按顺序执行,返回一个可能已修改的实体或一个反应式包装类型。

实体回调通常按 API 类型进行分离。这种分离意味着同步 API 只考虑同步实体回调,而反应式实现只考虑反应式实体回调。

实体回调 API 是从 Spring Data Commons 2.2 开始引入的。它是应用实体修改的推荐方式。现有的特定于存储的 ApplicationEvents 仍然在调用可能已注册的 EntityCallback 实例 **之前** 发布。

实现实体回调

EntityCallback 通过其泛型类型参数直接与其域类型相关联。每个 Spring Data 模块通常都附带一组预定义的 EntityCallback 接口,涵盖实体生命周期。

EntityCallback 的结构
@FunctionalInterface
public interface BeforeSaveCallback<T> extends EntityCallback<T> {

	/**
	 * Entity callback method invoked before a domain object is saved.
	 * Can return either the same or a modified instance.
	 *
	 * @return the domain object to be persisted.
	 */
	(1)
	T onBeforeSave(T entity, (2)
		String collection); (3)
}
1 BeforeSaveCallback 特定方法,在保存实体之前调用。返回一个可能已修改的实例。
2 实体在持久化之前。
3 一些特定于存储的参数,例如实体持久化的 集合
反应式 EntityCallback 的结构
@FunctionalInterface
public interface ReactiveBeforeSaveCallback<T> extends EntityCallback<T> {

	/**
	 * Entity callback method invoked on subscription, before a domain object is saved.
	 * The returned Publisher can emit either the same or a modified instance.
	 *
	 * @return Publisher emitting the domain object to be persisted.
	 */
	(1)
	Publisher<T> onBeforeSave(T entity, (2)
		String collection); (3)
}
1 BeforeSaveCallback 是一个特定方法,在订阅时调用,用于在保存实体之前执行。它会发出一个可能被修改过的实例。
2 实体在持久化之前。
3 一些特定于存储的参数,例如实体持久化的 集合
可选的实体回调参数由实现的 Spring Data 模块定义,并从 EntityCallback.callback() 的调用站点推断。

根据您的应用程序需求实现适合的接口,如下面的示例所示。

BeforeSaveCallback 示例
class DefaultingEntityCallback implements BeforeSaveCallback<Person>, Ordered {      (2)

	@Override
	public Object onBeforeSave(Person entity, String collection) {                   (1)

		if(collection == "user") {
		    return // ...
		}

		return // ...
	}

	@Override
	public int getOrder() {
		return 100;                                                                  (2)
	}
}
1 根据您的需求实现回调。
2 如果存在多个针对同一域类型的实体回调,则可以对它们进行排序。排序遵循最低优先级。

注册实体回调

如果 EntityCallback bean 在 ApplicationContext 中注册,则存储特定实现会将其拾取。大多数模板 API 已经实现了 ApplicationContextAware,因此可以访问 ApplicationContext

以下示例解释了有效的实体回调注册集合。

EntityCallback Bean 注册示例
@Order(1)                                                           (1)
@Component
class First implements BeforeSaveCallback<Person> {

	@Override
	public Person onBeforeSave(Person person) {
		return // ...
	}
}

@Component
class DefaultingEntityCallback implements BeforeSaveCallback<Person>,
                                                           Ordered { (2)

	@Override
	public Object onBeforeSave(Person entity, String collection) {
		// ...
	}

	@Override
	public int getOrder() {
		return 100;                                                  (2)
	}
}

@Configuration
public class EntityCallbackConfiguration {

    @Bean
    BeforeSaveCallback<Person> unorderedLambdaReceiverCallback() {   (3)
        return (BeforeSaveCallback<Person>) it -> // ...
    }
}

@Component
class UserCallbacks implements BeforeConvertCallback<User>,
                                        BeforeSaveCallback<User> {   (4)

	@Override
	public Person onBeforeConvert(User user) {
		return // ...
	}

	@Override
	public Person onBeforeSave(User user) {
		return // ...
	}
}
1 BeforeSaveCallback@Order 注解中获取其顺序。
2 BeforeSaveCallback 通过 Ordered 接口实现获取其顺序。
3 BeforeSaveCallback 使用 lambda 表达式。默认情况下无序,最后调用。请注意,由 lambda 表达式实现的回调不会公开类型信息,因此使用不可分配的实体调用它们会影响回调吞吐量。使用 classenum 为回调 bean 启用类型过滤。
4 将多个实体回调接口组合到一个实现类中。

存储特定的 EntityCallbacks

Spring Data MongoDB 使用 EntityCallback API 来支持其审计,并对以下回调做出反应。

表 1. 支持的实体回调
回调 方法 描述 顺序

ReactiveBeforeConvertCallback BeforeConvertCallback

onBeforeConvert(T entity, String collection)

在将域对象转换为 org.bson.Document 之前调用。

Ordered.LOWEST_PRECEDENCE

ReactiveAfterConvertCallback AfterConvertCallback

onAfterConvert(T entity, org.bson.Document target, String collection)

在加载域对象后调用。
可以在从org.bson.Document读取域对象后修改它。

Ordered.LOWEST_PRECEDENCE

ReactiveAuditingEntityCallback AuditingEntityCallback

onBeforeConvert(Object entity, String collection)

标记可审计实体为已创建已修改

100

ReactiveBeforeSaveCallback BeforeSaveCallback

onBeforeSave(T entity, org.bson.Document target, String collection)

在保存域对象之前调用。
可以修改目标,以持久化包含所有映射实体信息的Document

Ordered.LOWEST_PRECEDENCE

ReactiveAfterSaveCallback AfterSaveCallback

onAfterSave(T entity, org.bson.Document target, String collection)

在保存域对象之前调用。
可以修改域对象,在保存后返回包含所有映射实体信息的Document

Ordered.LOWEST_PRECEDENCE