11

该文档讨论了依赖注入,但并未真正展示它是如何完成的。

文档也没有完成并且有一堆占位符:http: //ktor.io/getting-started.html

我尝试以接受参数(这是我的依赖项)的方式创建我的 main 函数,但是当我调用withTestApplication. 我查看了应用程序代码,发现应用程序接受了一个配置对象,但我不知道如何更改该配置对象以在其中注入一些依赖项。

package org.jetbrains.ktor.application

/**
 * Represents configured and running web application, capable of handling requests
 */
class Application(val environment: ApplicationEnvironment) : ApplicationCallPipeline() {
    /**
     * Called by host when [Application] is terminated
     */
    fun dispose() {
        uninstallAllFeatures()
    }
}

/**
 * Convenience property to access log from application
 */
val Application.log get() = environment.log

在使用我的测试代码中,withTestApplication我有类似下面的内容:

@Test
internal fun myTest() = withTestApplication (Application::myMain)

如果我使用参数(我需要模拟和注入的参数)调用,上述操作withTestApplication将失败。myMain

更新:

问题是在我的请求处理中,我正在使用一个连接到外部其他 Web 服务并执行一些请求的依赖类,我需要一种能够注入它的方法,因此在我的测试中我可以存根/模拟它并更改它基于我的测试用例的行为。

4

4 回答 4

13

Ktor 没有内置的依赖注入机制。如果您需要使用 DI,则需要使用您喜欢的任何框架,例如 Guice。它看起来像这样:

fun Application.module() {
  Guice.createInjector(MainModule(this))
}

// Main module, binds application and routes
class MainModule(private val application: Application) : AbstractModule() {
    override fun configure() {
        bind(Application::class.java).toInstance(application)
        ... other bindings ...
    }
}

通过这种方式,您可以将应用程序组合委托给 Guice,并将其构建为任何其他应用程序。例如,您可能会像这样编写应用程序的不同部分:

class Hello @Inject constructor(application: Application) {
  init {
    application.routing {
        get("/") {
            call.respondText("Hello")
        }
    }
  }
}

然后将其绑定到主模块中:

bind(Hello::class.java).asEagerSingleton()

asEagerSingleton需要这样 Guice 将急切地创建它,因为没有其他服务会查询它。

于 2017-09-14T08:18:02.200 回答
12

Koin的简单示例

1)首先,定义我们的产品和测试依赖项:

val prodModule = module {
    single<IFirstService> { RealFirstService() }
    single<ISecondService> { RealSecondService() }
}

val testModule = module {
    single<IFirstService> { FakeFirstService() }
    single<ISecondService> { FakeSecondService() }
}

2)然后在app启动前添加DI初始化:

fun main(args: Array<String>) {
    startKoin(listOf(prodModule))
    embeddedServer(Netty, commandLineEnvironment(args)).start(true)
}

3)在应用程序或路由中使用注入:

fun Application.apiModule() {
    val firstService: IFirstService by inject()
    val secondService: ISecondService by inject()
    ...
    routing {
        someApi(inject(), inject())
    }
}

4)(可选)对于测试,只需在运行测试之前在 testModule 中添加初始化:

fun testApp(test: TestApplicationEngine.() -> Unit) {
    withTestApplication({
        ... // configure your test app here

        stopKoin() // Need to stop koin and restart after other tests
        startKoin(listOf(testModule)) // Init with test DI

        apiModule() // Run you application
    })
}

// And run tests
@Test
fun `get events`() = testApp {
    // do tests
}

就这样!

于 2019-01-11T00:53:12.520 回答
0

Kodein DI具有良好的 Ktor 支持,请参阅相应文档了解更多详细信息。

文档中的示例代码:

fun main(args: Array<String>) {
    embeddedServer(Netty, port = 8080) {
        di { 
            bind<Random>() with singleton { SecureRandom() } 
        }

        routing {
            get("/") {
                val random by di().instance<Random>() 
                /* logic here */
            }
        }
   }.start(true)
}
于 2021-01-03T12:49:53.950 回答
-1

在对 koin kodein 和 daggers 进行了一些实验之后,我们最终使用了带有 ktor的 spring-context。它就像一个魅力。

第 1 步:在我们的 gradle 文件中:

implementation(group = "org.springframework", name = "spring-context", version = "5.3.5")

随意使用您喜欢的任何 spring-context 版本进行更改。

第 2 步:我们定义了我们@Component的 s、@Configurations、@Beans 等,就像我们使用任何 spring 应用程序一样。

第 3 步:在我们的 main 方法中,我们有一些胶水代码来显式初始化 DI 上下文并使用它来初始化 ktor:

val ctx = AnnotationConfigApplicationContext("YOUR.ROOT.PACKAGE.GOES.HERE")
val someBean = ctx.getBean(SomeBean::class.java) // you can get any bean you need to use in your top-level glue code

// ... go ahead with KTOR configuration as usual. You can access any bean using the ctx variable.

当然,我们显式与 spring 上下文交互的胶水代码只执行一次。项目的其余部分由使用常规 spring-context 方式相互引用的组件组成。

于 2021-04-01T09:45:49.130 回答