实体回调

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 表达式实现的回调不公开类型信息,因此使用不可分配的实体调用这些回调会影响回调吞吐量。使用 `class` 或 `enum` 为回调 bean 启用类型过滤。
4 将多个实体回调接口组合到一个实现类中。