實體回呼

Spring Data 基礎架構提供 Hook,用於在調用某些方法之前和之後修改實體。這些所謂的 EntityCallback 實例提供了一種便捷的方式,以回呼方式檢查和潛在修改實體。
EntityCallback 看起來很像特製化的 ApplicationListener。某些 Spring Data 模組發布特定於儲存體的事件 (例如 BeforeSaveEvent),允許修改給定的實體。在某些情況下,例如使用不可變類型時,這些事件可能會造成問題。此外,事件發布依賴於 ApplicationEventMulticaster。如果使用非同步 TaskExecutor 配置它,可能會導致不可預測的結果,因為事件處理可能會 Fork 到一個 Thread 上。

實體回呼提供與同步和反應式 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 BeforeSaveCallback 使用 Lambda 表達式。預設為無序,且最後調用。請注意,由 Lambda 表達式實作的回呼不會公開類型資訊,因此使用不可指派的實體調用這些回呼會影響回呼吞吐量。使用 classenum 來為回呼 Bean 啟用類型篩選。
4 在單一實作類別中組合多個實體回呼介面。