HI, KTor now has built-in Dependency Injection. It...
# ktor
l
HI, KTor now has built-in Dependency Injection. It's tied to the Application module. Is there an elegant way to get application dependencies inside classes without passing the application instance itself in the constructor?
b
There's a few ways for injection using constructors via reflection... From the provider:
Copy code
dependencies {
    provide(MyImplementation::class)
    provide(::MyImplementation)
}
From the resolver:
Copy code
dependencies.create(MyImplemenation::class)
From configuration:
Copy code
application:
    dependencies:
          - com.example.MyImplemenation
l
thank you @Bruce Hamilton, but If the MyImplementation class is complex enough and also pulls in dependencies within itself?
b
yep, it'll just try each constructor in sequence and resolve each argument from the registry
l
seems it is not so suitable as KoinComponent in Koin
b
could you provide an example of what you mean here? I'm not familiar with that part of the Koin API... I take it you're using the interop with Ktor DI?
l
i mean i have to pass application instance or dependencies in every class constructor where i want to use it.. Application.kt
Copy code
fun main(args: Array<String>) = EngineMain.main(args)

fun Application.cfg(key: String, default: String = "") = environment.config.propertyOrNull(key)?.getString() ?: default

fun Application.module(testing: Boolean = false) = runBlocking {
    val log = KotlinLogging.logger {}

    val buildNumber = System.getenv("BUILD_NUMBER") ?: TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()).toString()

    log.info { "rut-bot build $buildNumber starting..." }

    configureDependencies()

    startRutbot()
}
Dependencies.kt
Copy code
fun Application.configureDependencies() {
    val app = this

    dependencies {
        provide<Boolean> { false } // Debug mode off by default

        provide("user") { app.cfg("rutbot.user") }

        provide<RutConnectService> { RutConnectService(app) }
        provide<LocalState> { LocalState(app) }
        provide<RutBot> { RutBot(app) }
    }
}
RutConnectService.kt
Copy code
class RutConnectService(private val application: Application) {
    val log = KotlinLogging.logger("RutConnect")

    private val user = runBlocking { application.dependencies.resolve<String>("user") }
using Koin it can be done with KoinComponent interface Dependencies.kt
Copy code
fun Application.configureDependencies() {

    val rutModule = module {
        single(named(Parameters.key)) { cfg("ktor.environment", "dev") }

        single(named(Parameters.debug)) { cfg("ktor.environment.debug", "false").toBoolean() }

        single<RutConnectService> { RutConnectService() }
        single<UserService> { UserService() }
        single<LocalState> { LocalState() }
        single<TradeService> { TradeService() }
    }

    install(Koin) {
        slf4jLogger()
        modules(rutModule)
    }
}
RutConnectService.kt
Copy code
class RutConnectService: KoinComponent {
    val log = KotlinLogging.logger("RutConnect")

    private val localState: LocalState by inject()
    private val tradeService: TradeService by inject()
    private val userService: UserService by inject()
b
Oh, you can just write
dependencies.provide(RutConnectService::class)
for providing then expose the properties in the constructor. This decouples your class from Ktor and it's DI as well.
l
thank you very much, and how to resolve named string binding in than case, i mean something like this:
provide<String>("user") { "tester.bill" }
b
When using the constructor reflection it'll need a
@Named("user")
annotation on the property. With the lambda config it's just a string arg on resolve like
provide { MyService(resolve('user")) }