實體回撥

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 使用 lambda 表示式的 `BeforeSaveCallback`。預設無序並最後呼叫。請注意,透過 lambda 表示式實現的回撥不暴露型別資訊,因此使用不可賦值的實體呼叫它們會影響回撥吞吐量。使用 `class` 或 `enum` 為回撥 bean 啟用型別過濾。
4 在單個實現類中組合多個實體回撥介面。