依賴注入
所需依賴項:io.ktor:ktor-server-di
依賴注入 (DI) 外掛允許您註冊服務和配置物件一次,並將它們注入到您的應用程式模組、外掛、路由以及專案中的其他元件。Ktor 的 DI 旨在與其現有的應用程式生命週期自然整合,開箱即用地支援作用域和結構化配置。
新增依賴項
要使用 DI,請在您的建置腳本中包含 ktor-server-di
artifact:
基本依賴項註冊
您可以使用 Lambda 表達式、函式參考或建構函式參考來註冊依賴項:
dependencies {
// 基於 Lambda 的
provide<GreetingService> { GreetingServiceImpl() }
// 函式參考
provide<GreetingService>(::GreetingServiceImpl)
provide(BankServiceImpl::class)
provide(::createBankTeller)
// 將 Lambda 註冊為依賴項
provide<() -> GreetingService> { { GreetingServiceImpl() } }
}
基於配置的依賴項註冊
您可以在配置檔案中使用 classpath 參考,以宣告式方式配置依賴項。這支援函式和類別參考:
# application.yaml
ktor:
application:
dependencies:
- com.example.RepositoriesKt.provideDatabase
- com.example.UserRepository
database:
connectionUrl: postgres://localhost:3037/admin
// Repositories.kt
fun provideDatabase(@Property("database.connectionUrl") connectionUrl: String): Database =
PostgresDatabase(connectionUrl)
class UserRepository(val db: Database) {
// 實作
}
Ktor 使用 DI 容器自動解析建構函式和函式參數。在特殊情況下,例如僅憑類型不足以區分值時,您可以使用 @Property
或 @Named
等註解來覆寫或明確綁定參數。如果省略,Ktor 將嘗試使用 DI 容器按類型解析參數。
依賴項解析與注入
解析依賴項
要解析依賴項,您可以使用屬性委託或直接解析:
// 使用屬性委託
val service: GreetingService by dependencies
// 直接解析
val service = dependencies.resolve<GreetingService>()
非同步依賴項解析
為了支援非同步載入,您可以使用 suspend 函式:
suspend fun Application.installEvents() {
val kubernetesConnection: EventsConnection = dependencies.resolve() // 暫停直到提供
}
suspend fun Application.loadEventsConnection() {
dependencies.provide {
connect(property<KubernetesConfig>("app.events"))
}
}
DI 外掛將自動暫停 resolve()
呼叫,直到所有依賴項準備就緒。
注入到應用程式模組
您可以透過在模組函式中指定參數,將依賴項直接注入到應用程式模組。Ktor 將根據類型匹配從 DI 容器中解析這些依賴項。
首先,在配置的 dependencies
部分註冊您的依賴項提供者:
ktor:
application:
dependencies:
- com.example.PrintStreamProviderKt.stdout
modules:
- com.example.LoggingKt.logging
以下是依賴項提供者和模組函式的樣子:
// com.example.PrintStreamProvider.kt
fun stdout(): () -> PrintStream = { System.out }
// com.example.Logging.kt
fun Application.logging(printStreamProvider: () -> PrintStream) {
dependencies {
provide<Logger> { SimpleLogger(printStreamProvider()) }
}
}
使用 @Named
注入特定鍵值的依賴項:
fun Application.userRepository(@Named("mongo") database: Database) {
// 使用名為 "mongo" 的依賴項
}
屬性與配置注入
使用 @Property
直接注入配置值:
connection:
domain: api.example.com
path: /v1
protocol: https
val connection: Connection = application.property("connection")
這簡化了結構化配置的使用,並支援基本類型的自動解析。
進階依賴項功能
可選和可空依賴項
使用可空類型優雅地處理可選依賴項:
// 使用屬性委託
val config: Config? by dependencies
// 或直接解析
val config = dependencies.resolve<Config?>()
協變泛型
Ktor 的 DI 系統支援類型協變性,當類型參數是協變時,這允許將值注入為其超類型之一。這對於處理子類型的集合和介面特別有用。
dependencies {
provide<List<String>> { listOf("one", "two") }
}
// 由於支援類型參數協變性,這將起作用
val stringList: List<CharSequence> by dependencies
// 這也將起作用
val stringCollection: Collection<CharSequence> by dependencies
協變性也適用於非泛型超類型:
dependencies {
provide<BufferedOutputStream> { BufferedOutputStream(System.out) }
}
// 這之所以有效,是因為 BufferedOutputStream 是 OutputStream 的子類型
val outputStream: OutputStream by dependencies
限制
儘管 DI 系統支援泛型類型的協變性,但它目前不支援跨類型參數子類型解析參數化類型。這表示您不能使用比已註冊類型更具體或更通用的類型來檢索依賴項。
例如,以下程式碼將無法解析:
dependencies {
provide<Sink<CharSequence>> { CsqSink() }
}
// 將無法解析
val charSequenceSink: Sink<String> by dependencies
資源生命週期管理
當應用程式關閉時,DI 外掛會自動處理生命週期和清理。
AutoCloseable 支援
預設情況下,任何實作 AutoCloseable
的依賴項在應用程式停止時都會自動關閉:
class DatabaseConnection : AutoCloseable {
override fun close() {
// 關閉連線,釋放資源
}
}
dependencies {
provide<DatabaseConnection> { DatabaseConnection() }
}
自訂清理邏輯
您可以透過指定 cleanup
函式來定義自訂清理邏輯:
dependencies {
provide<ResourceManager> { ResourceManagerImpl() } cleanup { manager ->
manager.releaseResources()
}
}
帶鍵的作用域清理
使用 key
來管理具名資源及其清理:
dependencies {
key<Closer>("second") {
provide { CustomCloser() }
cleanup { it.closeMe() }
}
}
依賴項以宣告的反向順序進行清理,以確保正確的拆解。
使用依賴注入進行測試
DI 外掛提供了簡化測試的工具。您可以在載入應用程式模組之前覆寫依賴項:
fun test() = testApplication {
application {
dependencies.provide<MyService> {
MockService()
}
loadServices()
}
}
在測試中載入配置
在測試中使用 configure()
可以輕鬆載入配置檔案:
fun test() = testApplication {
// 從預設配置檔案路徑載入屬性
configure()
// 載入多個帶有覆寫的檔案
configure("root-config.yaml", "test-overrides.yaml")
}
測試引擎會忽略衝突的宣告,讓您可以自由覆寫。