实体回调

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 在单个实现类中组合多个实体回调接口。