相依性解析
必要的相依性:io.ktor:ktor-server-di
程式碼範例: server-di
在您註冊相依性之後,您可以從相依性注入 (DI) 容器中解析它們並將其注入應用程式程式碼中。
您可以使用屬性委託或直接解析從 DI 容器明確地解析相依性。
使用屬性委託
使用屬性委託時,當屬性首次被存取時,相依性會以延遲方式解析:
val service: GreetingService by dependencies使用直接解析
直接解析會立即傳回相依性,或者掛起直到其可用為止:
val service = dependencies.resolve<GreetingService>()參數解析
當解析建構函式或函式時,Ktor 會使用 DI 容器解析參數。預設情況下,參數是依型別解析的。
如果基於型別的解析不足,您可以使用註解來明確綁定參數。
使用具名相依性
使用 @Named 註解來解析以指定名稱註冊的相依性:
fun Application.userRepository(@Named("mongo") database: Database) {
// 使用名為 "mongo" 的相依性
}使用配置屬性
使用 @Property 註解從應用程式配置中注入一個值:
package com.example
import io.ktor.server.plugins.di.annotations.Property
fun provideDatabase(
@Property("database.connectionUrl") connectionUrl: String
): Database = PostgresDatabase(connectionUrl)
open class UserRepository(val db: Database)在上述範例中,database.connectionUrl 屬性是從應用程式配置中解析的:
database:
connectionUrl: postgres://localhost:5432/admin非同步相依性解析
為了支援非同步載入,您可以使用掛起函式:
data class EventsConnection(val connected: Boolean)
suspend fun Application.installEvents() {
val conn: EventsConnection = dependencies.resolve()
log.info("Events connection ready: $conn")
}
suspend fun Application.loadEventsConnection() {
dependencies.provide {
delay(200) // 模擬非同步工作
EventsConnection(true)
}
}DI 外掛程式會自動掛起 resolve() 呼叫,直到所有相依性都就緒。
將相依性注入應用程式模組
您可以透過在模組函式中指定參數,將相依性直接注入應用程式模組。Ktor 會根據型別比對從 DI 容器中解析這些相依性。
首先,在配置檔案中的 ktor.application.dependencies 群組中註冊您的相依性提供者:
ktor:
application:
dependencies:
- com.example.PrintStreamProviderKt.stdout
modules:
- com.example.LoggingKt.logging為您想要注入的相依性定義具有參數的相依性提供者和模組函式。然後,您可以直接在模組函式中使用注入的相依性:
package com.example
import java.io.PrintStream
fun stdout(): () -> PrintStream = { System.out }package com.example
import io.ktor.server.application.*
import io.ktor.server.plugins.di.dependencies
import java.io.PrintStream
class Logger(private val out: PrintStream) {
fun log(message: String) {
out.println("[LOG] $message")
}
}
fun Application.logging(printStreamProvider: () -> PrintStream) {
dependencies {
provide<Logger> {
Logger(printStreamProvider())
}
}
}進階相依性解析
選填與可為 null 的相依性
使用可為 null 的型別來優雅地處理選填相依性:
// 使用屬性委託
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