0
# Core Dependency Injection
1
2
This document covers the core dependency injection capabilities provided by the `Koin` class and how to resolve dependencies throughout your application.
3
4
## Overview
5
6
Koin provides several ways to inject and resolve dependencies:
7
- **Direct resolution** using `get()` - Immediately resolve an instance
8
- **Lazy injection** using `inject()` - Defer resolution until first access
9
- **Nullable variants** - Handle optional dependencies gracefully
10
- **Collection resolution** - Get all instances of a type
11
- **Instance declaration** - Register instances at runtime
12
13
All injection methods work through the `Koin` context, which acts as the main dependency resolution hub.
14
15
## Koin Context
16
17
The `Koin` class is the central dependency injection context:
18
19
```kotlin { .api }
20
class Koin {
21
// Core resolution methods
22
inline fun <reified T : Any> get(
23
qualifier: Qualifier? = null,
24
noinline parameters: ParametersDefinition? = null
25
): T
26
27
inline fun <reified T : Any> inject(
28
qualifier: Qualifier? = null,
29
mode: LazyThreadSafetyMode = KoinPlatformTools.defaultLazyMode(),
30
noinline parameters: ParametersDefinition? = null
31
): Lazy<T>
32
33
// Nullable variants
34
inline fun <reified T : Any> getOrNull(/* ... */): T?
35
inline fun <reified T : Any> injectOrNull(/* ... */): Lazy<T?>
36
37
// Non-reified variants
38
fun <T> get(clazz: KClass<*>, qualifier: Qualifier?, parameters: ParametersDefinition?): T
39
fun <T> getOrNull(clazz: KClass<*>, qualifier: Qualifier?, parameters: ParametersDefinition?): T?
40
41
// Collection resolution
42
inline fun <reified T> getAll(): List<T>
43
44
// Runtime declaration
45
inline fun <reified T> declare(
46
instance: T,
47
qualifier: Qualifier? = null,
48
secondaryTypes: List<KClass<*>> = emptyList(),
49
allowOverride: Boolean = true
50
)
51
}
52
```
53
54
## Direct Resolution with get()
55
56
Use `get()` to immediately resolve and obtain an instance:
57
58
### Basic Usage
59
60
```kotlin
61
import org.koin.core.*
62
import org.koin.dsl.*
63
64
// Define dependencies
65
val appModule = module {
66
single<Repository> { DatabaseRepository() }
67
factory<UseCase> { GetDataUseCase(get()) }
68
}
69
70
val koinApp = koinApplication { modules(appModule) }
71
val koin = koinApp.koin
72
73
// Direct resolution
74
val repository: Repository = koin.get()
75
val useCase: UseCase = koin.get()
76
```
77
78
### With Qualifiers
79
80
```kotlin
81
import org.koin.core.qualifier.named
82
83
val networkModule = module {
84
single<ApiService>(named("v1")) { ApiServiceV1() }
85
single<ApiService>(named("v2")) { ApiServiceV2() }
86
}
87
88
val koin = koinApplication { modules(networkModule) }.koin
89
90
// Resolve with qualifier
91
val apiV1: ApiService = koin.get(named("v1"))
92
val apiV2: ApiService = koin.get(named("v2"))
93
```
94
95
### With Parameters
96
97
```kotlin
98
import org.koin.core.parameter.parametersOf
99
100
val configModule = module {
101
factory<DatabaseConfig> { params ->
102
val url: String = params.get()
103
val port: Int = params.get()
104
DatabaseConfig(url, port)
105
}
106
}
107
108
val koin = koinApplication { modules(configModule) }.koin
109
110
// Resolve with parameters
111
val config: DatabaseConfig = koin.get {
112
parametersOf("localhost", 5432)
113
}
114
```
115
116
### Non-Reified Resolution
117
118
For cases where you need to resolve using `KClass`:
119
120
```kotlin
121
import kotlin.reflect.KClass
122
123
val repository = koin.get<Repository>(
124
clazz = Repository::class,
125
qualifier = named("primary"),
126
parameters = { parametersOf("config") }
127
)
128
```
129
130
## Lazy Injection with inject()
131
132
Use `inject()` for lazy resolution - the instance is created only when first accessed:
133
134
### Basic Lazy Injection
135
136
```kotlin
137
val serviceModule = module {
138
single<HeavyService> { HeavyService() }
139
}
140
141
val koin = koinApplication { modules(serviceModule) }.koin
142
143
// Lazy injection - HeavyService not created yet
144
val service: Lazy<HeavyService> = koin.inject()
145
146
// HeavyService created here on first access
147
val actualService = service.value
148
```
149
150
### Thread Safety Modes
151
152
Control how lazy instances handle concurrent access:
153
154
```kotlin
155
import org.koin.mp.KoinPlatformTools
156
157
// Default platform-specific mode
158
val service1: Lazy<Service> = koin.inject()
159
160
// Synchronized access (thread-safe)
161
val service2: Lazy<Service> = koin.inject(mode = LazyThreadSafetyMode.SYNCHRONIZED)
162
163
// No synchronization (fastest, but not thread-safe)
164
val service3: Lazy<Service> = koin.inject(mode = LazyThreadSafetyMode.NONE)
165
166
// Publication safety (thread-safe initialization, but allows multiple writes)
167
val service4: Lazy<Service> = koin.inject(mode = LazyThreadSafetyMode.PUBLICATION)
168
```
169
170
### Lazy Injection with Parameters
171
172
```kotlin
173
val parameterizedService: Lazy<ConfigurableService> = koin.inject {
174
parametersOf("production", 30)
175
}
176
177
// Parameters evaluated when lazy value is accessed
178
val service = parameterizedService.value
179
```
180
181
## Nullable Resolution
182
183
Handle optional dependencies gracefully:
184
185
### Nullable Direct Resolution
186
187
```kotlin
188
val optionalModule = module {
189
single<RequiredService> { RequiredService() }
190
// OptionalService not defined
191
}
192
193
val koin = koinApplication { modules(optionalModule) }.koin
194
195
// Required service - will succeed
196
val required: RequiredService = koin.get()
197
198
// Optional service - returns null instead of throwing
199
val optional: OptionalService? = koin.getOrNull()
200
201
if (optional != null) {
202
// Use optional service
203
optional.doSomething()
204
}
205
```
206
207
### Nullable Lazy Injection
208
209
```kotlin
210
val optionalService: Lazy<OptionalService?> = koin.injectOrNull()
211
212
// Check if service is available when accessed
213
val service = optionalService.value
214
if (service != null) {
215
service.performAction()
216
}
217
```
218
219
### Nullable with Qualifiers and Parameters
220
221
```kotlin
222
val optionalConfig: DatabaseConfig? = koin.getOrNull(
223
qualifier = named("test"),
224
parameters = { parametersOf("test-db") }
225
)
226
```
227
228
## Collection Resolution
229
230
Get all registered instances of a specific type:
231
232
### Basic Collection Resolution
233
234
```kotlin
235
val pluginModule = module {
236
single<Plugin> { PluginA() }
237
single<Plugin> { PluginB() }
238
single<Plugin> { PluginC() }
239
}
240
241
val koin = koinApplication { modules(pluginModule) }.koin
242
243
// Get all Plugin instances
244
val allPlugins: List<Plugin> = koin.getAll()
245
246
allPlugins.forEach { plugin ->
247
plugin.initialize()
248
}
249
```
250
251
### With Different Qualifiers
252
253
```kotlin
254
val handlerModule = module {
255
single<EventHandler>(named("auth")) { AuthEventHandler() }
256
single<EventHandler>(named("payment")) { PaymentEventHandler() }
257
factory<EventHandler>(named("logging")) { LoggingEventHandler() }
258
}
259
260
val koin = koinApplication { modules(handlerModule) }.koin
261
262
// Gets all EventHandler instances regardless of qualifiers
263
val allHandlers: List<EventHandler> = koin.getAll()
264
```
265
266
## Runtime Instance Declaration
267
268
Register instances dynamically at runtime:
269
270
### Basic Declaration
271
272
```kotlin
273
val koin = koinApplication { modules(emptyModule) }.koin
274
275
// Declare an instance at runtime
276
val runtimeConfig = RuntimeConfig("dynamic-value")
277
koin.declare(runtimeConfig)
278
279
// Now it can be resolved
280
val config: RuntimeConfig = koin.get()
281
```
282
283
### Declaration with Qualifiers
284
285
```kotlin
286
// Declare with specific qualifier
287
koin.declare(
288
instance = DatabaseConnection("prod-db"),
289
qualifier = named("production")
290
)
291
292
// Resolve with qualifier
293
val prodDb: DatabaseConnection = koin.get(named("production"))
294
```
295
296
### Multiple Type Bindings
297
298
```kotlin
299
interface Repository
300
interface ReadOnlyRepository
301
class DatabaseRepository : Repository, ReadOnlyRepository
302
303
val dbRepo = DatabaseRepository()
304
305
// Bind to multiple types
306
koin.declare(
307
instance = dbRepo,
308
secondaryTypes = listOf(ReadOnlyRepository::class),
309
allowOverride = false
310
)
311
312
// Can resolve as either type
313
val repo: Repository = koin.get()
314
val readOnly: ReadOnlyRepository = koin.get()
315
```
316
317
### Override Control
318
319
```kotlin
320
// First declaration
321
koin.declare(UserService("v1"))
322
323
// Override existing - will succeed with allowOverride = true
324
koin.declare(
325
instance = UserService("v2"),
326
allowOverride = true
327
)
328
329
// This would fail with allowOverride = false
330
// koin.declare(UserService("v3"), allowOverride = false) // Exception!
331
```
332
333
## Global Context Access
334
335
For applications using a global Koin context:
336
337
### Starting Global Context
338
339
```kotlin { .api }
340
import org.koin.core.context.*
341
342
// Start global context
343
fun startKoin(koinApplication: KoinApplication): KoinApplication
344
fun startKoin(appDeclaration: KoinAppDeclaration): KoinApplication
345
346
// Stop global context
347
fun stopKoin(): Unit
348
349
// Module management in global context
350
fun loadKoinModules(module: Module): Unit
351
fun loadKoinModules(modules: List<Module>): Unit
352
fun unloadKoinModules(module: Module): Unit
353
fun unloadKoinModules(modules: List<Module>): Unit
354
```
355
356
### Using Global Context
357
358
```kotlin
359
import org.koin.core.context.*
360
import org.koin.dsl.*
361
362
// Start global context
363
startKoin {
364
modules(appModule)
365
}
366
367
// The global context is now available through KoinPlatformTools
368
// Components can access it without explicit Koin reference
369
```
370
371
## Scoped Resolution
372
373
Dependencies can also be resolved within specific scopes (covered in detail in [Scope Management](scoping.md)):
374
375
```kotlin
376
val scopedModule = module {
377
scope<UserSession> {
378
scoped<UserData> { UserData(get()) }
379
}
380
}
381
382
val koin = koinApplication { modules(scopedModule) }.koin
383
val userScope = koin.createScope<UserSession>("user-123")
384
385
// Resolve within scope
386
val userData: UserData = userScope.get()
387
```
388
389
## Best Practices
390
391
### 1. Prefer Lazy Injection for Expensive Resources
392
393
```kotlin
394
// Good - lazy initialization
395
val heavyService: Lazy<HeavyService> = koin.inject()
396
397
// Use only when needed
398
if (someCondition) {
399
heavyService.value.performOperation()
400
}
401
```
402
403
### 2. Use Nullable Variants for Optional Dependencies
404
405
```kotlin
406
class OptionalFeatureService(
407
private val required: RequiredService,
408
private val optional: OptionalService? = null
409
) {
410
fun performAction() {
411
required.execute()
412
optional?.enhanceExecution()
413
}
414
}
415
416
val service = OptionalFeatureService(
417
required = koin.get(),
418
optional = koin.getOrNull()
419
)
420
```
421
422
### 3. Handle Collections Appropriately
423
424
```kotlin
425
// Initialize all plugins
426
val plugins: List<Plugin> = koin.getAll()
427
plugins.forEach { it.initialize() }
428
429
// Process with handlers
430
val handlers: List<EventHandler> = koin.getAll()
431
val event = Event("user.login")
432
handlers.forEach { handler ->
433
handler.handle(event)
434
}
435
```
436
437
### 4. Runtime Declaration for Dynamic Configuration
438
439
```kotlin
440
class ConfigurableApplication {
441
fun configureEnvironment(environment: String, koin: Koin) {
442
when (environment) {
443
"development" -> {
444
koin.declare(DevConfig(), named("env"))
445
}
446
"production" -> {
447
koin.declare(ProdConfig(), named("env"))
448
}
449
}
450
}
451
}
452
```
453
454
This flexible resolution system enables clean dependency injection patterns while maintaining type safety and supporting various usage scenarios from simple direct resolution to complex lazy initialization strategies.