实体回调
Spring Data 基础设施提供了钩子,用于在调用特定方法之前和之后修改实体。这些所谓的 EntityCallback
实例提供了一种便捷的方式,可以以回调方式检查并可能修改实体。
一个 EntityCallback
看上去很像一个专门的 ApplicationListener
。一些 Spring Data 模块会发布特定于存储的事件(例如 BeforeSaveEvent
),允许修改给定的实体。在某些情况下,例如处理不可变类型时,这些事件可能会导致问题。此外,事件发布依赖于 ApplicationEventMulticaster
。如果使用异步 TaskExecutor
配置它,可能会导致不可预测的结果,因为事件处理可能会分叉到另一个线程。
实体回调提供了与同步和响应式 API 的集成点,以保证在处理链中定义好的检查点按顺序执行,并返回一个可能被修改的实体或一个响应式包装类型。
实体回调通常按 API 类型分隔。这种分隔意味着同步 API 只考虑同步实体回调,而响应式实现只考虑响应式实体回调。
实体回调 API 是在 Spring Data Commons 2.2 中引入的。它是应用实体修改的推荐方式。现有的特定于存储的 |
实现实体回调
一个 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 | 在同一个实现类中结合多个实体回调接口。 |