Koin is a kotlin DSL. So no annotations, reflection, or compiler magic are needed. Basically it just instantiates things for you on demand and uses normal kotlin features like lambda functions, receiver objects, and property delegation.
You can inject a property by defining it as a koin injected property:
class SomeClass {
val foo by koinContext.inject<Foo>()
}
And this is how you could create your koin context:
class Bar()
class Foo(bar: Bar)
val myModule = module {
single { Bar() }
// get() knows the type from the argument position at compile time
// and it looks the object from the context by type
single { Foo(get()) }
}
// trigger this in your main or at application startup
startKoin {
modules(myModule)
}
// if you want to use inject, you can use a global variable for getting at the context
val koinContext by lazy { GlobalContext.get() }
Modules can be tied to life cycles on Android and there are a few more features like being clever about co-routine scopes. But there's not a lot more to it in terms to using it. Very simple code to write. Low overhead. It's all just function calls.
Spring has a similar kotlin bean dsl that was added a while ago that you can use as an alternative to annotation processing (which is much slower).