Koin is a pragmatic lightweight dependency injection framework for Kotlin developers, providing core dependency injection functionality for Kotlin Multiplatform projects.
—
Koin provides the KoinComponent interface and extension functions to integrate dependency injection into your Kotlin classes. This allows any class to participate in the Koin dependency injection system.
interface KoinComponent {
fun getKoin(): Koin
}The KoinComponent interface is a marker interface that provides access to the Koin instance. Any class implementing this interface can use Koin's dependency injection features.
class UserController : KoinComponent {
private val userService: UserService = get()
fun createUser(userData: UserData) {
userService.create(userData)
}
}inline fun <reified T : Any> KoinComponent.get(
qualifier: Qualifier? = null,
noinline parameters: ParametersDefinition? = null
): TImmediately resolves and returns an instance of the requested type.
Parameters:
qualifier: Optional qualifier to distinguish between multiple implementationsparameters: Optional parameters to pass to the definition functioninline fun <reified T : Any> KoinComponent.inject(
qualifier: Qualifier? = null,
mode: LazyThreadSafetyMode = KoinPlatformTools.defaultLazyMode(),
noinline parameters: ParametersDefinition? = null
): Lazy<T>Returns a lazy delegate that resolves the instance on first access.
Parameters:
qualifier: Optional qualifier to distinguish between multiple implementationsmode: Lazy thread safety mode (platform-specific default)parameters: Optional parameters to pass to the definition functionclass OrderService : KoinComponent {
// Direct injection - resolved immediately
private val database: Database = get()
// Lazy injection - resolved on first access
private val emailService: EmailService by inject()
// With qualifiers
private val primaryCache: Cache = get(named("primary"))
private val secondaryCache: Cache by inject(named("secondary"))
// With parameters
private fun getValidator(type: String): Validator {
return get { parametersOf(type) }
}
}interface KoinScopeComponent : KoinComponent {
val scope: Scope
}KoinScopeComponent extends KoinComponent to provide scoped dependency injection. Classes implementing this interface have their own scope for managing lifecycle-aware dependencies.
fun <T : Any> T.getScopeId(): ScopeID
fun <T : Any> T.getScopeName(): TypeQualifier
fun <T : KoinScopeComponent> T.createScope(
scopeId: ScopeID = getScopeId(),
source: Any? = null,
scopeArchetype: TypeQualifier? = null
): Scope
fun <T : KoinScopeComponent> T.createScope(source: Any? = null): Scope
fun <T : KoinScopeComponent> T.getScopeOrNull(): Scope?class UserSession : KoinScopeComponent {
override val scope: Scope = createScope()
// Inject from the component's scope
private val sessionData: SessionData by scope.inject()
private val userPreferences: UserPreferences = scope.get()
fun logout() {
// Clean up scoped dependencies
scope.close()
}
}class MyService : KoinComponent {
// Lazy properties
private val repository by inject<UserRepository>()
private val config by inject<AppConfig>()
private val logger by inject<Logger>(named("service"))
fun doWork() {
logger.info("Starting work")
val users = repository.findAll()
// ...
}
}While Koin primarily uses property injection, you can also resolve dependencies in constructors:
class MyService : KoinComponent {
private val repository: UserRepository
private val config: AppConfig
init {
repository = get()
config = get()
}
}class MyService : KoinComponent {
private val logger: Logger by lazy {
if (BuildConfig.DEBUG) {
get<Logger>(named("debug"))
} else {
get<Logger>(named("production"))
}
}
}class ParametersHolder(private val values: Array<out Any?>) {
inline fun <reified T> get(index: Int = 0): T
inline fun <reified T> getOrNull(index: Int = 0): T?
fun size(): Int
}
fun parametersOf(vararg values: Any?): ParametersHolderclass DocumentService : KoinComponent {
fun processDocument(documentType: String, metadata: Map<String, Any>) {
val processor: DocumentProcessor = get {
parametersOf(documentType, metadata)
}
processor.process()
}
}
// In module definition
val documentModule = module {
factory<DocumentProcessor> { params ->
val type = params.get<String>(0)
val metadata = params.get<Map<String, Any>>(1)
when (type) {
"pdf" -> PDFProcessor(metadata)
"word" -> WordProcessor(metadata)
else -> GenericProcessor(metadata)
}
}
}Common exceptions when using components:
class NoBeanDefFoundException(message: String) : RuntimeException(message)
class InstanceCreationException(message: String, cause: Throwable?) : RuntimeException(message, cause)
class ClosedScopeException(message: String) : RuntimeException(message)class MyService : KoinComponent {
fun safeGetService(): UserService? {
return try {
get<UserService>()
} catch (e: NoBeanDefFoundException) {
null
}
}
}class UserControllerTest : KoinComponent {
@Before
fun setup() {
startKoin {
modules(testModule)
}
}
@Test
fun testUserCreation() {
val controller = UserController()
// Controller will use test dependencies
controller.createUser(testUserData)
}
@After
fun teardown() {
stopKoin()
}
}Install with Tessl CLI
npx tessl i tessl/maven-io-insert-koin--koin-core