Spring Fu 的演進

工程 | Sébastien Deleuze | 2018年10月02日 | ...

藉著在 SpringOne platform(在此我做了關於Spring Fu的第一個演講)和 Kotlinconf 之間的短暫間隙,我想對該專案的演進做一個概述,總結當前狀態並分享下一步可能的計劃。

六月初,我宣佈了一個名為 Spring Fu 的新實驗性專案,旨在透過Kotlin DSL和函式式配置來試驗一種配置Spring應用程式的新型API。

我必須承認,我沒有預料到隨之而來的巨大反饋浪潮,我想感謝Spring社群如此熱烈的歡迎。此後,我繼續致力於該專案,希望能將這個基於原生Spring Framework API的第一個POC轉化為Spring新函式式功能的一個孵化器。

Kofu 配置

Kotlin DSL 現在基於Spring Boot基礎設施,並稱為Kofu(取自Kotlin和functional)。它允許使用Kotlin DSL和lambda而不是註解來配置Spring Boot應用程式,並具有以下特點:

  • 透過Kotlin DSL進行顯式配置
  • 基於函式式使用的Spring Boot基礎設施
  • 不依賴於類路徑檢測來啟用功能
  • 宣告式和命令式兼備
  • 更快的啟動和更低的記憶體消耗
  • 最少地使用反射和註解
  • 純粹的 Lambda,無 CGLIB 代理

使用Kofu配置的典型Spring Boot應用程式如下所示:

val app = application {
  import(beans)
  listener<ApplicationReadyEvent> {
    ref<UserRepository>().init()
  }
  properties<SampleProperties>("sample")
  server {
    port = if (profiles.contains("test")) 8181 else 8080
    mustache()
    codecs {
      string()
      jackson {
        indentOutput = true
      }
    }
    import(::routes)
  }
  mongodb {
    embedded()
  }
}

val beans = beans {
  bean<UserRepository>()
  bean<UserHandler>()
}

fun routes(userHandler: UserHandler) = router {
  GET("/", userHandler::listView)
  GET("/api/user", userHandler::listApi)
  GET("/conf", userHandler::conf)
}

fun main() = app.run()

為了使用它,您“只需”為Spring Boot 2.1應用程式新增一個org.springframework.fu:spring-boot-kofu依賴項。如當前版本號0.0.2所示,請注意,目前API尚不穩定,不適用於生產環境,且其範圍僅限於Spring Boot支援的部分功能。

但請隨時嘗試,提供反饋,並嘗試這種新的Spring Boot應用程式配置方式,它具有函式式特性,可以透過IDE自動補全進行發現,並且有文件說明

Kofu並不比自動配置更好或更差,它只是不同。我相信這可能是Spring Boot觸達那些偏好更顯式配置模型以及來自Kotlin、Go、Node或Ruby等其他背景的開發者的途徑。

Jafu 配置

最初僅限於Kotlin,我收到的一個主要反饋是來自對這種顯式DSL方法也感興趣的Java開發人員,所以我研究了一個Java等價物,並最終得到了這個Jafu(取自Java和functional)DSL。

public class JafuApplication {

  public static SpringApplication app = application(app -> {
    app.beans(beans -> {
      beans.bean(SampleService.class);
      beans.bean(SampleHandler.class);
    });
    app.server(server -> server.router(router -> {
      SampleHandler sampleHandler = app.ref(SampleHandler.class);
      router.GET("/", sampleHandler::hello);
      router.resources("/**", new ClassPathResource("static/"));
    }));
  });

  public static void main (String[] args) {
    app.run(args);
  }
}

目前這只是一個POC,但我計劃很快達到與Kofu的功能對等,並並行開發這兩個DSL。缺乏型別安全構建器reified型別引數擴充套件機制將使Jafu比Kofu更冗長且可擴充套件性更差,但儘管有這些限制,我認為Jafu仍然非常不錯且可用。

對於僅對使用函式式Bean註冊基礎設施帶來的效能提升感興趣的使用者,值得注意的是,Dave Syer目前正在試驗一些解決方案,這些方案可以使我們能夠利用函式式Bean註冊的效率,同時仍使用基於註解的常規Spring Boot應用程式。

將Spring應用程式作為原生可執行檔案執行

GraalVM是Oracle開發的一個新的虛擬機器,它除了其他功能外,還可以透過Substratevm將JVM位元組碼編譯為原生可執行檔案。

Spring Framework 5.1為GraalVM原生映象提供了一些初步支援,但我們仍處於故事的開端。GraalVM團隊需要修復Dave提出的各種問題,才能讓一切正常工作,而生態系統也需要適應這個擁有不同約束和特性的新平臺。

但GraalVM團隊正在傾聽我們的反饋,Spring應用程式的支援在最近幾個月取得了顯著進展。現在已經可以編譯一個基本的Spring Boot響應式應用程式(使用Kofu配置)作為原生可執行檔案,它幾乎可以瞬間啟動!

協程支援

如前所述,Spring Fu的主要目標是孵化將整合到當前頂級專案(如Spring Framework、Spring Data和Spring Boot)中的功能。

Spring Fu目前正在為Spring WebFlux和Spring Data孵化協程支援,以便能夠以更命令式的方式利用Spring響應式堆疊。這主要面向那些希望利用此類堆疊的可伸縮性,而又不需要響應式API全部強大功能的開發者。

class UserRepository(private val mongo: CoroutinesMongoTemplate) {
	suspend fun count(): Long = mongo.count<User>()
	suspend fun findAll(): List<User> = mongo.findAll<User>()
	suspend fun findOne(id: String): User? = mongo.findById<User>(id)
	suspend fun deleteAll() = mongo.remove<User>()
	suspend fun save(user: User): User? = mongo.save(user)
}

請注意,雖然協程在Kotlin 1.3中被認為是最終版本,但在kotlinx-coroutines中仍然缺少一個重要的部分,因為它沒有提供任何處理冷流的型別。我們需要這個缺失的抽象才能用協程API暴露我們的響應式基礎,更多詳情請參閱kotlinx.coroutines#254

請注意,我們應該能夠在協程和Reactor型別之間傳遞上下文,以便支援諸如響應式安全和事務等非常強大的用例。

透過其出色的spring-kotlin-coroutine專案貢獻了原始Spring協程支援的Konrad Kaminski將很快加入Spring Fu,與我一起致力於此功能。

結論

我剛剛釋出了Spring Fu 0.0.2,它提供了改進的Kofu DSL並引入了函式引數的自動裝配。請隨時嘗試並提供反饋。

即將釋出的Spring Fu 0.0.3將提供Kofu和Jafu配置之間的功能對等。

我們已經有10多位社群貢獻者向Spring Fu提交了Pull Request,所以如果您有想法,也許下一個就是您 ;-)

期待在即將舉行的Spring Fu講座中與您見面,地點包括JFuture(明斯克)、Spring Fest(東京)和 Devoxx(安特衛普)。

獲取 Spring 新聞通訊

透過 Spring 新聞通訊保持聯絡

訂閱

領先一步

VMware 提供培訓和認證,助您加速進步。

瞭解更多

獲得支援

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案,只需一份簡單的訂閱。

瞭解更多

即將舉行的活動

檢視 Spring 社群所有即將舉行的活動。

檢視所有