Spring Data 2020.0 - 最新功能總覽

工程 | Christoph Strobl | 2020 年 11 月 06 日 | ...

Spring Data 2020.0 基於 Spring Framework 5.3 發布,其中包含了各個模組涵蓋的多項新功能。除了在里程碑公告中發布重點外,我們還希望透過一系列的部落格文章,更詳細地介紹這些新功能。這些文章將涵蓋以下內容:

  • Data Commons 中的反應式上下文存取功能。
  • R2DBC 中 Spring R2DBC 的生命週期回調和基線。
  • 通用儲存庫和特定於儲存的 Redis 快取指標。
  • Neo4J 反應式儲存庫。.
  • Spring Data for Apache Geode 的分頁儲存庫 API。
  • Spring Data Elasticsearch 的 Geoshape、索引和反應式搜尋改進。
  • MongoDB 中的部分篩選器和彙總提示。
  • JDBC 模組的 @Bean 列對應器。
  • Apache Cassandra 抽象的每個語句金鑰空間定義。
  • 宣告式 Couchbase N1QL 查詢。

在第一部分中,我們將花時間介紹一些通用主題以及通用模組的某些方面,這些方面會對特定於儲存的實作產生影響。

我們先從最明顯的變更開始:版本控制方案。在遵循詞彙排序(尊重著名的電腦科學家)超過 7 年後,我們切換到基於 CalVer 的版本控制方案。這將主要影響那些透過 spring-data-releasetrain 成品管理 Spring Data 成品的版本的人,該成品已變更為 spring-data-bom 以及新的方案。

org.springframework.data
spring-data-bom
2020.0.0

各個模組繼續使用數字版本控制方案。

儘管進行了這些基礎架構變更,但目前版本仍明確關注擴展反應式故事、更多應用程式洞察力、指標以及在使用 GraalVM 原生映像時提供更好的開發人員體驗。儘管如此,我們還是花時間在這裡和那裡添加了一些小功能,希望讓您的日常工作更加方便。

其中一項增強功能是在投影中新增了對 java.lang.Optional 的支援。這不是一個改變遊戲規則的變更,但如果您想避免 null 值,它仍然很有用。

interface User {

	String getNickname();
	Optional<byte[]> getPicture();
}

在研究效能洞察力(稍後會詳細介紹)時,我們現在將儲存庫初始化延遲到第一次使用時,這加快了應用程式的啟動時間。

我們還花時間 Delombok 整個生產程式碼庫,以提高可讀性和除錯能力。

反應式上下文存取

此版本為完成反應式故事貢獻了另一個重要組成部分。反應式上下文存取的加入,讓我們可以在反應式流程中處理反應式稽核和 SpEL 上下文擴展評估等功能。到目前為止,稽核和 SpEL 在一定程度上已經成為可能。缺少的部分是存取上下文詳細資訊的能力,例如通常由 WebFlux 安排中的 Spring Security 提供的驗證主體。

談到上下文,您有兩個選擇

  • 將上下文與調用一起傳遞
  • 頻外儲存上下文詳細資訊(例如,使用 ThreadLocal

稽核和 SpEL 上下文擴展的命令式變體依賴於 ThreadLocal 儲存。但是,訂閱反應式管道會消除對管道將實現的線程的任何假設。因此,此方法變得無法使用。這就是第二個選項發揮作用的地方,將上下文詳細資訊與調用一起傳遞。Project Reactor 的 Context 功能允許將上下文資料附加到訂閱。要存取上下文,需要一個 API 才能使用 Publisher 類型。因此,在此版本中,我們引入了 ReactiveAuditorAwareReactiveEvaluationContextExtension 的反應式 API 變體來進行稽核。

interface ReactiveAuditorAware<T> {  
	Mono<T> getCurrentAuditor();  
}

interface ReactiveEvaluationContextExtension extends ExtensionIdAware {  
	Mono<? extends EvaluationContextExtension> getExtension();  
}

您可以將這兩個介面的實作公開為 Spring bean。Spring Data 會擷取它們並將它們用於反應式稽核,分別用於上下文擴展。

反應式稽核

反應式稽核將可稽核實體與時間戳記和目前使用者(「稽核員」)相關聯。標記有 @CreatedBy@LastModifiedDate 的屬性會在插入和更新時由 Spring Data 填入。在先前的版本中,標記有 @CreatedDate@LastModifiedDate 的屬性會透過實體回調進行更新。從此版本開始,稽核員會使用專用的實體回調從 ReactiveAuditorAware 傳播到實體中。

使用 Spring Security 的 ReactiveSecurityContextHolderReactiveAuditorAware 的範例實作可能如下所示:

class ReactiveUsernameAuditor implements ReactiveAuditorAware<String>{  

  	@Override  
  	public Mono<String> getCurrentAuditor() {  
    	  	return ReactiveSecurityContextHolder.getContext()  
              	  	.map(SecurityContext::getAuthentication)  
              	  	.map(Authentication::getPrincipal)  
              	  	.map(Object::toString);  
  	}
}

您需要兩個步驟才能為您的儲存模組啟用反應式稽核。首先,透過您的儲存特定 @EnableReactive…Auditing 註釋啟用反應式稽核。其次,註冊一個 ReactiveAuditorAware bean。Cassandra、Elasticsearch、MongoDB、Neo4j 和 R2DBC 支援反應式稽核。

以下組態程式碼片段顯示如何啟用反應式稽核。您可以在 Github 上找到完整範例

@Configuration  
@EnableReactiveCassandraAuditing  
class ApplicationConfiguration {  
  
	@Bean  
	ReactiveAuditorAware<String> reactiveAuditorAware() {  
		return () -> Mono.just("the-static-auditor-name");  
	}  
}

請注意,該範例會產生一個名為 the-static-auditor-name 的靜態稽核員。使用從上下文衍生的動態稽核員需要其他元件,例如 Spring Security 和 WebFlux。請注意,先前的幾個儲存透過 @Enable…Auditing 啟用了反應式稽核的日期部分。隨著 @EnableReactive…Auditing 的引入,情況不再如此,@Enable…Auditing 僅啟用命令式稽核。如果您需要反應式稽核,請確保相應地採用您的組態。

反應式 Spring 運算式語言("SpEL")上下文擴展

SpEL 上下文擴展代表一個 SPI,允許插入應用程式或程式庫特定的擴展,這些擴展提供可以使用 SpEL 運算式在各種地方使用的功能。上下文擴展可以保留應用程式特定的狀態、公開網域特定的功能,或允許存取與傳入的應用程式請求相關聯的上下文資料。SpEL 上下文功能的一個典型範例是存取與經過驗證或匿名應用程式使用者發出的 HTTP 請求相關聯的安全性上下文。

與反應式稽核類似,任何每個請求的上下文都需要從訂閱者 Context 中擷取。在此版本中,我們引入了 ReactiveEvaluationContextProvider,它是 EvaluationContextProvider 的反應式變體。ReactiveEvaluationContextProvider 允許延遲 EvaluationContext 擷取。透過傳回 Mono,上下文提供者可以存取訂閱者 Context。反應式上下文擴展需要實作 ReactiveEvaluationContextExtension,以便它們可以參與延遲上下文擴展解析。反應式擴展可以擷取上下文資料,並將其傳遞給實際的 EvaluationContextExtension,該擴展提供 Spring Data 的 SpEL 評估機制的功能。

請注意,反應式 SpEL 支援僅適用於反應式儲存庫查詢方法。其他元件(例如,MongoDB @Document 或 Cassandra @Table 名稱中的集合名稱)中的 SpEL 運算式會在不考慮反應式上下文擴展的情況下解析,因為該 API 無法存取訂閱者 Context

SpEL 上下文擴展解析的改進

自 Spring Data 成立以來,如果查詢包含至少一個 SpEL 運算式,它會嘗試在 SpEL 運算式處理之前解析所有已註冊的 SpEL 內容擴充。內容解析可能會很耗時。此外,有時內容可能不存在。請考慮以下查詢方法:

@Query("SELECT * FROM person WHERE tenant = :#{tenantId}")
List<Person> findAllPeopleForTenant();

@Query("SELECT * FROM person WHERE tenant = :#{tenantId} or 1=?#{hasRole('ROLE_ADMIN') ? 1 : 0")
List<Person> findAllPeople();

這兩個查詢方法都使用 SpEL 運算式,而第二個查詢方法引用了 Spring Security 的 SpEL 內容擴充。當調用僅考慮 tenantId 的第一個方法 (findAllPeopleForTenant) 時,Spring Data 也會載入 Spring Security 的擴充。如果在安全上下文之外調用該方法,則方法調用會失敗,因為 Spring Security 無法解析安全上下文。在 CommandLineRunnerApplicationEventLister 或生命週期方法(例如 @PostConstruct)中進行調用是沒有給定安全上下文的典型示例。

class MyComponent {

  @Autowired PersonRepository repository;
  
  @PostConstruct
  public void postConstruct() {
    TenantIdHolder.setCurrentTenant(4711);

    // this method fails with an exception because it attempts to resolve
    // the security context although it's not required in for the actual query.
    repository.findAllPeopleForTenant(); 
  }
}

隨著 Reactive SpEL 內容擴充的引入,我們改進了 SPI,使其僅解析實際需要的擴充。org.springframework.data.spel.ExpressionDependencies 執行 SpEL 運算式的靜態分析,並收集符號(屬性引用和方法調用)。透過 EvaluationContextProvider 取得 EvaluationContext 接受 ExpressionDependencies 以檢查哪些擴充提供了這些符號,並且僅載入可以滿足依賴性要求的擴充。此變更對大多數應用程式應該是透明的,因為它僅觸及內部程式碼。請注意,如果查詢僅引用可以解析的上下文功能,則依賴性分析能夠在例如 @PostConstruct 中安排執行啟用 SpEL 的查詢。查詢中未涉及的內容擴充不再嘗試進行解析,並且該變更可能會反映在您的應用程式中。此最佳化適用於命令式和反應式程式碼路徑,並應提高效能。

儲存庫指標 (Repository Metrics)

深入了解應用程式的運行情況對某些企業至關重要。持久層(尤其是)可能是效能問題的罪魁禍首,原因可能是網路或伺服器端的慢速查詢執行。儲存庫指標現在透過直接連結到 Spring Data 執行基礎架構,為您提供資料存取層運行時效能的真實來源。附加到儲存庫的監聽器會收到對介面進行的每次調用的通知,從而提供有關儲存庫本身、調用的方法、傳遞的參數以及執行結果(成功、錯誤和取消訊號)的資訊,但不包括實際結果。

對於同步儲存庫介面,測量從查詢方法調用時間開始,其中包括實際解析潛在的註解查詢並評估透過 SpEL 運算式提供值的擴充所花費的時間(如前一節所述),並在返回潛在轉換的結果時結束。

對於反應式儲存庫,實際訂閱標記調用入口點,而完成或取消訊號完成測量。

目前,設定不是最方便的,因為它需要一個 BeanPostProcessor 來操作儲存庫 factory bean,方法是新增調用監聽器。但是,未來的 Spring Boot 版本將提供對 actuator 端點中包含的儲存庫指標的專用支援。

class RepositoryMetricsPostProcessor implements BeanPostProcessor { 
   public Object postProcessBeforeInitialization(Object bean, String beanName) {

     if (bean instanceof RepositoryFactoryBeanSupport) {
         RepositoryFactoryBeanSupport<?, ?, ?> repositoryFactoryBean = (...) bean;      
         repositoryFactoryBean.addRepositoryFactoryCustomizer(repositoryFactory -> {
         	repositoryFactory.addInvocationListener(System.out::println);
         });
     }
   return bean;
   }
}

為了讓您了解它的實際作用,我們準備了一個小的 指標範例 供您參考。它將調用持續時間列印到控制台。

PersonRepository.save(Object): 2 ms – SUCCESS

PersonRepository.save(Object): 2 ms – SUCCESS

PersonRepository.findAll(): 32 ms – SUCCESS

PersonRespository.findByName(String): 1 ms - SUCCESS

GraalVM 原生映像檔 (Native Images)

在談論指標和效能時,人們可能會很快想到 scale-to-zero 場景、啟動速度和 GraalVM 原生映像檔。Spring Data 團隊一直在這方面努力,從而改善了編譯使用 Spring Data 儲存庫的原生映像檔時的開發人員體驗。這些努力涵蓋了用於停用某些功能的簡單切換開關,例如用於最佳化實體實例化場景的程式碼產生、編譯提示,以及作為 spring-graalvm-native 專案一部分的 SpringDataComponentProcessor。元件處理器檢查 Spring Data 儲存庫、網域類型和方法簽章。根據該資訊,它會將所有需要的代理、資源和反射配置新增到原生映像檔,使其能夠正常工作。這還包括任何特定於商店的註解以及命令式和反應式介面的自訂實作。如果您感到好奇,請試試看

至此,我們結束了本系列的第一部分。請關注此空間,以獲取有關您喜歡的商店的更新,並查看 Spring Data 範例,以縮短發布前的時間。

取得 Spring 電子報

與 Spring 電子報保持聯繫

訂閱

搶先一步

VMware 提供培訓和認證,以加速您的進度。

了解更多

取得支援

Tanzu Spring 在一個簡單的訂閱中提供對 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位檔案。

了解更多

即將舉行的活動

查看 Spring 社群中所有即將舉行的活動。

查看全部