實體回撥

Spring Data 基礎設施提供了在呼叫某些方法之前和之後修改實體的鉤子。這些所謂的 EntityCallback 例項提供了一種方便的方式,以回撥方式檢查並可能修改實體。
一個 EntityCallback 看起來很像一個專用的 ApplicationListener。一些 Spring Data 模組釋出特定於儲存的事件(例如 BeforeSaveEvent),允許修改給定的實體。在某些情況下,例如處理不可變型別時,這些事件可能會導致問題。此外,事件釋出依賴於 ApplicationEventMulticaster。如果將其配置為非同步 TaskExecutor,由於事件處理可能會分派到另一個執行緒,可能會導致不可預測的結果。

實體回撥提供了與同步和響應式 API 的整合點,以確保在處理鏈中預定義的檢查點按順序執行,返回可能已修改的實體或響應式包裝型別。

實體回撥通常按 API 型別區分。這種區分意味著同步 API 只考慮同步實體回撥,而響應式實現只考慮響應式實體回撥。

實體回撥 API 是隨 Spring Data Commons 2.2 引入的。它是應用實體修改的推薦方式。現有的特定於儲存的 ApplicationEvent 仍然會在呼叫潛在註冊的 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() 的呼叫點推斷出來。

實現適合您應用程式需求的介面,如下例所示

示例 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 在單個實現類中組合多個實體回撥介面。