|
此版本仍在开发中,尚未被视为稳定版。如需最新稳定版本,请使用 Spring Data MongoDB 5.0.4! |
生命周期事件
MongoDB 映射框架包含多个 org.springframework.context.ApplicationEvent 事件,您的应用程序可以通过在 ApplicationContext 中注册特殊的 Bean 来响应这些事件。
由于基于 Spring 的 ApplicationContext 事件基础设施,其他产品(例如 Spring Integration)可以轻松接收这些事件,因为它们是 Spring 应用程序中广为人知的事件机制。
实体生命周期事件可能会带来较高的开销,当加载大型结果集时,您可能会注意到性能特征发生变化。 您可以在Template API上禁用生命周期事件。
为了在对象进入转换过程(即将您的领域对象转换为 org.bson.Document)之前对其进行拦截,您可以注册一个 AbstractMongoEventListener 的子类,并重写其中的 onBeforeConvert 方法。
当事件被触发时,您的监听器会被调用,并在对象进入转换器之前接收到该领域对象。
以下示例展示了如何实现这一点:
public class BeforeConvertListener extends AbstractMongoEventListener<Person> {
@Override
public void onBeforeConvert(BeforeConvertEvent<Person> event) {
... does some auditing manipulation, set timestamps, whatever ...
}
}
在对象进入数据库之前对其进行拦截,您可以注册 AbstractMongoEventListener 的子类并重写 onBeforeSave 方法。当事件被分发时,您的监听器将被调用,并传入领域对象和转换后的 com.mongodb.Document。以下示例展示了如何实现:
public class BeforeSaveListener extends AbstractMongoEventListener<Person> {
@Override
public void onBeforeSave(BeforeSaveEvent<Person> event) {
… change values, delete them, whatever …
}
}
在您的 Spring ApplicationContext 中声明这些 Bean 后,每当事件被分发时,它们就会被调用。
AbstractMappingEventListener 上的回调:
-
onBeforeConvert:在MongoTemplate的insert、insertList和save操作中,对象被Document转换为MongoConverter之前调用。 -
onBeforeSave:在MongoTemplate的insert、insertList和save操作中,于将Document插入或保存到数据库之前调用。 -
onAfterSave:在MongoTemplate的insert、insertList和save操作中,于将Document插入或保存到数据库之后调用。 -
onAfterLoad:在MongoTemplate的find、findAndRemove、findOne和getCollection方法中,从数据库检索出Document后调用。 -
onAfterConvert:在MongoTemplate的find、findAndRemove、findOne和getCollection方法中,当从数据库检索到的Document已被转换为 POJO 后调用。
生命周期事件仅针对根级别类型发出。
作为文档根内属性使用的复杂类型,除非它们是使用 @DBRef 注解的文档引用,否则不会触发事件发布。 |
生命周期事件依赖于一个 ApplicationEventMulticaster,在使用 SimpleApplicationEventMulticaster 的情况下,可以配置一个 TaskExecutor,因此无法保证事件处理的具体时机。 |
实体回调
Spring Data 基础设施提供了在调用某些方法之前和之后修改实体的钩子。
这些所谓的EntityCallback实例提供了一种便捷的方式,以回调风格检查并可能修改实体。
An EntityCallback看起来非常像一个专门的ApplicationListener.
一些 Spring Data 模块会发布特定存储的事件(例如BeforeSaveEvent) 允许修改给定的实体。在某些情况下,例如处理不可变类型时,这些事件可能会引发问题。
此外,事件发布依赖于ApplicationEventMulticaster. 如果使用异步方式配置该功能TaskExecutor它可能导致不可预测的结果,因为事件处理可以分叉到线程上。
实体回调提供了与同步和响应式 API 的集成点,以确保在处理链中定义明确的检查点按顺序执行,并返回一个可能已被修改的实体或响应式包装类型。
实体回调通常按 API 类型进行区分。这种区分意味着同步 API 仅考虑同步实体回调,而响应式实现仅考虑响应式实体回调。
|
实体回调(Entity Callback)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() 的调用点推断得出。 |
实现符合您应用程序需求的接口,如下例所示:
BeforeSaveCallbackclass 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 | 在单个实现类中组合多个实体回调接口。 |
存储特定 EntityCallbacks
Spring Data MongoDB 使用 EntityCallback API 来提供审计支持,并对以下回调作出响应。
| 回调 | 方法 | 描述 | 订单 |
|---|---|---|---|
|
|
在领域对象转换为 |
|
|
|
在加载领域对象后调用。 |
|
|
|
标记一个可审计实体为已创建或已修改 |
100 |
|
|
在保存领域对象之前调用。 |
|
|
|
在保存领域对象之前调用。 |
|
Bean 验证
Spring Data MongoDB 支持对使用 https://xxx[Jakarta 验证注解] 标注的 MongoDB 实体进行 Bean 验证。
您可以通过在 Spring ValidatingEntityCallback 中注册 ReactiveValidatingEntityCallback 或用于响应式驱动程序的 ApplicationContext 来启用 Bean Validation,如下例所示:
-
Imperative
-
Reactive
@Configuration
class Config {
@Bean
public ValidatingEntityCallback validatingEntityCallback(Validator validator) {
return new ValidatingEntityCallback(validator);
}
}
@Configuration
class Config {
@Bean
public ReactiveValidatingEntityCallback validatingEntityCallback(Validator validator) {
return new ReactiveValidatingEntityCallback(validator);
}
}
如果你同时使用命令式和响应式编程,那么也可以同时启用两种回调。
使用基于 XML 的配置时,历史上,ValidatingMongoEventListener 会在配置 <mongo:mapping-converter> 时通过我们的命名空间处理器进行注册。
如果希望使用较新的实体回调(Entity Callback)变体,请确保不要使用 <mongo:mapping-converter>,否则 ValidatingMongoEventListener 和 ValidatingEntityCallback 两者都会被注册。 |