实体回调
Spring Data 基础设施提供了在某些方法调用前后修改实体的钩子。这些所谓的 EntityCallback 实例提供了一种便捷的方式,以回调风格检查和潜在地修改实体。
一个 EntityCallback 看起来非常像一个专门的 ApplicationListener。一些 Spring Data 模块会发布特定存储的事件(例如 BeforeSaveEvent),这些事件允许修改给定的实体。在某些情况下,比如处理不可变类型时,这些事件可能会引发问题。此外,事件发布依赖于 ApplicationEventMulticaster。如果将其配置为异步的 TaskExecutor,则可能导致不可预测的结果,因为事件处理可能会分叉到一个线程上。
实体回调(Entity Callbacks)提供了与同步和响应式 API 的集成点,以确保在处理链中明确定义的检查点上按顺序执行,并返回一个可能被修改的实体或一个响应式包装类型。
实体回调通常根据 API 类型进行分离。这种分离意味着同步 API 仅考虑同步实体回调,而响应式实现仅考虑响应式实体回调。
Entity Callback API 已在 Spring Data Commons 2.2 中引入。它是应用实体修改的推荐方式。现有的特定存储的 ApplicationEvents 仍会在调用可能注册的 EntityCallback 实例之前发布。
实现实体回调
EntityCallback 通过其泛型类型参数直接与其域类型相关联。每个 Spring Data 模块通常附带一组预定义的 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
}
BeforeSaveCallback在实体保存之前调用的特定方法。返回一个可能被修改的实例。实体在持久化之前的状态。
一些存储特定的参数,例如实体被持久化到的 collection。
@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
}
BeforeSaveCallback是在实体被保存之前,在订阅上调用的特定方法。它会发出一个可能被修改的实例。在持久化之前的实体。
一系列存储特定的参数,比如实体被持久化的 collection。
可选的实体回调参数由实现 Spring Data 模块定义,并从 EntityCallback.callback() 的调用站点推断得出。
根据您的应用程序需求实现接口,如下例所示:
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
	}
}
根据你的要求实现回调。
如果存在多个针对同一域类型的实体回调,可能会对其进行排序。排序遵循最低优先级。
注册实体回调
EntityCallback Bean 会被存储特定的实现类所选取,前提是它们在 ApplicationContext 中注册。大多数模板 API 已经实现了 ApplicationContextAware,因此可以访问 ApplicationContext。
以下示例解释了一组有效的实体回调注册:
@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 // ...
	}
}
BeforeSaveCallback通过@Order注解接收其顺序。BeforeSaveCallback通过Ordered接口实现接收其顺序。BeforeSaveCallback使用 lambda 表达式。默认无序且最后调用。请注意,通过 lambda 表达式实现的回调不会暴露类型信息,因此使用不可分配的实体调用这些回调会影响回调的吞吐量。使用class或enum来启用回调 bean 的类型过滤。在单个实现类中组合多个实体回调接口。