实体回调
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` 接口,涵盖实体生命周期。
@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 | 一些特定于存储的参数,例如实体持久化的 *集合*。 |
@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()` 的调用站点推断。 |
根据您的应用程序需求实现接口,如下例所示
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`
以下示例解释了一组有效的实体回调注册
@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 | 在一个实现类中组合多个实体回调接口。 |