实体回调

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 使用 lambda 表达式的 `BeforeSaveCallback`。默认无序并最后调用。请注意,通过 lambda 表达式实现的回调不暴露类型信息,因此使用不可赋值的实体调用它们会影响回调吞吐量。使用 `class` 或 `enum` 为回调 bean 启用类型过滤。
4 在单个实现类中组合多个实体回调接口。