0
# Scoping and Context
1
2
Context-aware dependency management with support for hierarchical scopes, context translation, and lifecycle management for fine-grained control over dependency lifecycles and sharing.
3
4
## Capabilities
5
6
### DIContext System
7
8
Core context system for providing scoped dependency resolution with type-safe context management.
9
10
```kotlin { .api }
11
/**
12
* Context definition with type and value for scoped dependency resolution
13
* @param C Type of the context value
14
*/
15
interface DIContext<C : Any> {
16
/** TypeToken representing the context type */
17
val type: TypeToken<in C>
18
19
/** The actual context value */
20
val value: C
21
22
/**
23
* Immediate context implementation with direct value
24
* @param type TypeToken for the context type
25
* @param value The context value
26
*/
27
data class Value<C : Any>(
28
override val type: TypeToken<in C>,
29
override val value: C
30
) : DIContext<C>
31
32
/**
33
* Lazy context implementation with deferred value evaluation
34
* @param type TypeToken for the context type
35
* @param getValue Function that provides the context value when needed
36
*/
37
class Lazy<C : Any>(
38
override val type: TypeToken<in C>,
39
val getValue: () -> C
40
) : DIContext<C> {
41
override val value: C by lazy(getValue)
42
}
43
44
companion object {
45
/**
46
* Create a context with immediate value
47
* @param type TypeToken for the context type
48
* @param value The context value
49
* @return DIContext instance
50
*/
51
operator fun <C : Any> invoke(type: TypeToken<in C>, value: C): DIContext<C>
52
53
/**
54
* Create a context with lazy value evaluation
55
* @param type TypeToken for the context type
56
* @param getValue Function that provides the context value
57
* @return DIContext instance with lazy evaluation
58
*/
59
operator fun <C : Any> invoke(type: TypeToken<in C>, getValue: () -> C): DIContext<C>
60
}
61
}
62
63
/**
64
* Create a DIContext from a typed value
65
* @param context The context value
66
* @return DIContext wrapping the value
67
*/
68
fun <C : Any> diContext(context: C): DIContext<C>
69
70
/**
71
* Create a DIContext with lazy evaluation
72
* @param getContext Function that provides the context value
73
* @return DIContext with lazy evaluation
74
*/
75
fun <C : Any> diContext(getContext: () -> C): DIContext<C>
76
```
77
78
### Scope System
79
80
Comprehensive scoping system for managing instance lifecycles and sharing patterns across different contexts.
81
82
```kotlin { .api }
83
/**
84
* Scope interface for managing instance lifecycles within contexts
85
* @param C Type of the context for this scope
86
*/
87
interface Scope<C> {
88
/**
89
* Get the registry for a specific context
90
* @param context The context value for this scope
91
* @return ScopeRegistry for managing instances in this context
92
*/
93
fun getRegistry(context: C): ScopeRegistry
94
}
95
96
/**
97
* Registry for storing and retrieving scoped instances
98
*/
99
interface ScopeRegistry {
100
/**
101
* Get or create an instance using the provided creator
102
* @param key Unique key for the instance
103
* @param sync Whether to synchronize access
104
* @param creator Function to create the instance if not found
105
* @return The stored or newly created instance
106
*/
107
fun <T> get(key: Any, sync: Boolean, creator: () -> T): T
108
109
/**
110
* Clear all instances from this registry
111
*/
112
fun clear()
113
}
114
115
/**
116
* Multi-item scope registry using concurrent map for thread safety
117
*/
118
class StandardScopeRegistry : ScopeRegistry {
119
// Implementation uses ConcurrentHashMap for thread-safe access
120
}
121
122
/**
123
* Single-item scope registry optimized for performance
124
*/
125
class SingleItemScopeRegistry : ScopeRegistry {
126
// Optimized for scopes that only hold one instance
127
}
128
129
/**
130
* Global scope not bound to any specific context
131
*/
132
class UnboundedScope : Scope<Any> {
133
// Provides global singleton behavior
134
}
135
136
/**
137
* Simple scope implementation without context dependency
138
*/
139
class NoScope : Scope<Any> {
140
// Basic scope without advanced features
141
}
142
143
/**
144
* Hierarchical scope with parent-child relationships
145
* @param C Child context type
146
* @param PC Parent context type
147
*/
148
class SubScope<C : Any, PC : Any>(
149
private val parentScope: Scope<PC>,
150
private val getParentContext: (C) -> PC
151
) : Scope<C>
152
```
153
154
### Scope Builders and Context Binding
155
156
DSL methods for creating scoped binding contexts and managing scope hierarchies.
157
158
```kotlin { .api }
159
/**
160
* Base builder interface for scoped and context bindings
161
* @param C The context type for this builder
162
*/
163
interface DI.BindBuilder<C : Any> {
164
/** The context type for all bindings in this DSL context */
165
val contextType: TypeToken<C>
166
167
/** Whether the context is explicitly set */
168
val explicitContext: Boolean
169
170
/**
171
* Builder with scope support for scoped singletons and multitons
172
* @param C The scope's context type
173
*/
174
interface WithScope<C : Any> : BindBuilder<C> {
175
/** The scope for all bindings in this DSL context */
176
val scope: Scope<C>
177
}
178
}
179
180
/**
181
* Create a scoped binding builder
182
* @param scope The scope to use for bindings
183
* @return BindBuilder with scope support
184
*/
185
fun <C : Any> DI.Builder.scoped(scope: Scope<C>): DI.BindBuilder.WithScope<C>
186
187
/**
188
* Create a context-aware binding builder
189
* @return BindBuilder for the specified context type
190
*/
191
inline fun <reified C : Any> DI.Builder.contexted(): DI.BindBuilder<C>
192
```
193
194
### Context Translation
195
196
Advanced context translation system for converting between different context types and scoped dependency resolution.
197
198
```kotlin { .api }
199
/**
200
* Interface for translating between different context types
201
* @param C Source context type
202
* @param S Target context type
203
*/
204
interface ContextTranslator<C, S> {
205
/**
206
* Translate a context for dependency resolution
207
* @param key The binding key being resolved
208
* @param context The source context
209
* @return Translated context or null if translation not possible
210
*/
211
fun translate(key: DI.Key<*, *, *>, context: C): S?
212
}
213
214
/**
215
* Create a context translator using a transformation function
216
* @param t Function that performs the context translation
217
* @return ContextTranslator instance
218
*/
219
fun <C, S> contextTranslator(t: DirectDI.(C) -> S?): ContextTranslator<C, S>
220
221
/**
222
* Create a context finder that locates context from DirectDI
223
* @param t Function that finds the context
224
* @return ContextTranslator that uses the finder function
225
*/
226
fun <S> contextFinder(t: DirectDI.() -> S): ContextTranslator<Any, S>
227
228
/**
229
* Register a context translator with the DI builder
230
* @param translator The context translator to register
231
*/
232
fun <C, S> DI.Builder.registerContextTranslator(translator: ContextTranslator<C, S>)
233
234
/**
235
* Register a context finder with the DI builder
236
* @param t Function that finds the context
237
*/
238
fun <S> DI.Builder.registerContextFinder(t: DirectDI.() -> S)
239
```
240
241
### JVM-Specific Scopes
242
243
Platform-specific scoping implementations optimized for JVM environments with advanced memory management.
244
245
```kotlin { .api }
246
/**
247
* Scope using weak references for automatic cleanup (JVM only)
248
* @param C Context type for the weak scope
249
*/
250
class WeakContextScope<C : Any> : Scope<C> {
251
// Uses WeakHashMap for automatic context cleanup
252
// when contexts are garbage collected
253
}
254
255
/**
256
* Reference makers for different memory management strategies (JVM only)
257
*/
258
// Thread-local references for thread-scoped singletons
259
val threadLocal: RefMaker
260
261
// Soft references for memory-sensitive caching
262
val softReference: RefMaker
263
264
// Weak references for automatic cleanup
265
val weakReference: RefMaker
266
```
267
268
### Context Operations
269
270
Advanced context manipulation and scoped dependency access patterns for fine-grained dependency control.
271
272
```kotlin { .api }
273
/**
274
* Create a new DIAware with different context
275
* @param context New context for scoped dependencies
276
* @param trigger Optional trigger for dependency resolution
277
* @return New DIAware instance with updated context
278
*/
279
fun DIAware.On(context: DIContext<*> = this.diContext, trigger: DITrigger? = this.diTrigger): DI
280
281
/**
282
* Create a new DIAware with typed context
283
* @param context Context value
284
* @param trigger Optional trigger for dependency resolution
285
* @return New DIAware instance with typed context
286
*/
287
fun <C : Any> DIAware.on(context: C, trigger: DITrigger? = this.diTrigger): DI
288
289
/**
290
* Create a new DirectDI with different context
291
* @param context Context value for scoped dependencies
292
* @return DirectDI instance with the specified context
293
*/
294
fun <C : Any> DirectDIAware.on(context: C): DirectDI
295
```
296
297
**Usage Examples:**
298
299
```kotlin
300
// Define context types
301
data class UserSession(val userId: String, val sessionId: String)
302
data class RequestContext(val requestId: String, val clientId: String)
303
304
// Create scoped DI with multiple contexts
305
val di = DI {
306
// Global singleton (no scope)
307
bind<ConfigService>() with singleton { ConfigServiceImpl() }
308
309
// Request-scoped dependencies
310
scoped(UnboundedScope).apply {
311
bind<RequestLogger>() with singleton { RequestLoggerImpl() }
312
}
313
314
// User session scoped dependencies
315
contexted<UserSession>().apply {
316
bind<UserPreferences>() with singleton {
317
UserPreferencesImpl(diContext.value.userId)
318
}
319
bind<UserCache>() with singleton {
320
UserCacheImpl(diContext.value.sessionId)
321
}
322
}
323
324
// JVM-specific: Thread-local scope for per-thread singletons
325
bind<Database>() with singleton(ref = threadLocal) {
326
DatabaseConnection()
327
}
328
329
// Context translator for automatic context conversion
330
registerContextTranslator<RequestContext, UserSession> { requestCtx ->
331
// Convert request context to user session context
332
val userId = userService.getUserId(requestCtx.clientId)
333
UserSession(userId, generateSessionId())
334
}
335
}
336
337
// Using scoped dependencies
338
class RequestHandler : DIAware {
339
override val di = di
340
341
fun handleRequest(request: HttpRequest) {
342
val requestContext = RequestContext(request.id, request.clientId)
343
344
// Switch to request context
345
val requestDI = on(requestContext)
346
347
// These use request-scoped context
348
val logger: RequestLogger by requestDI.instance()
349
val userPrefs: UserPreferences by requestDI.instance() // Auto-translated from RequestContext
350
val userCache: UserCache by requestDI.instance()
351
352
logger.info("Processing request ${request.id}")
353
val preferences = userPrefs.load()
354
userCache.store("last_request", request.timestamp)
355
}
356
}
357
358
// Custom scope implementation
359
class TimedScope(private val ttlMs: Long) : Scope<Any> {
360
private val registries = mutableMapOf<Any, TimedScopeRegistry>()
361
362
override fun getRegistry(context: Any): ScopeRegistry {
363
return registries.getOrPut(context) { TimedScopeRegistry(ttlMs) }
364
}
365
366
private class TimedScopeRegistry(private val ttlMs: Long) : ScopeRegistry {
367
private data class TimedEntry<T>(val value: T, val timestamp: Long)
368
private val cache = mutableMapOf<Any, TimedEntry<*>>()
369
370
override fun <T> get(key: Any, sync: Boolean, creator: () -> T): T {
371
val now = System.currentTimeMillis()
372
val entry = cache[key] as? TimedEntry<T>
373
374
return if (entry != null && (now - entry.timestamp) < ttlMs) {
375
entry.value
376
} else {
377
val newValue = creator()
378
cache[key] = TimedEntry(newValue, now)
379
newValue
380
}
381
}
382
383
override fun clear() = cache.clear()
384
}
385
}
386
387
// Using custom scopes
388
val timedDI = DI {
389
val fiveMinuteScope = TimedScope(5 * 60 * 1000) // 5 minutes TTL
390
391
scoped(fiveMinuteScope).apply {
392
bind<ExpensiveService>() with singleton {
393
ExpensiveServiceImpl().also {
394
it.loadData() // Expensive initialization
395
}
396
}
397
}
398
}
399
400
// Hierarchical scoping
401
class ApplicationService : DIAware {
402
override val di = DI {
403
// Application-level scope
404
bind<AppConfig>() with singleton { AppConfigImpl() }
405
406
// Module-level scope (child of application scope)
407
val moduleScope = SubScope(UnboundedScope) { _: ModuleContext -> Unit }
408
scoped(moduleScope).apply {
409
bind<ModuleService>() with singleton { ModuleServiceImpl(instance()) }
410
}
411
412
// Request-level scope (child of module scope)
413
contexted<RequestContext>().apply {
414
bind<RequestProcessor>() with singleton {
415
RequestProcessorImpl(instance(), instance()) // Gets from parent scopes
416
}
417
}
418
}
419
420
private val appConfig: AppConfig by instance() // Application scope
421
422
fun processRequest(requestContext: RequestContext) {
423
val requestDI = on(requestContext)
424
val processor: RequestProcessor by requestDI.instance() // Request scope
425
processor.process()
426
}
427
}
428
429
// JVM-specific weak reference scoping
430
val jvmDI = DI {
431
// Weak context scope - automatically cleans up when contexts are GC'd
432
val weakScope = WeakContextScope<UserSession>()
433
scoped(weakScope).apply {
434
bind<UserData>() with singleton {
435
UserDataImpl(diContext.value.userId)
436
}
437
}
438
439
// Different reference types for memory management
440
bind<ImageCache>() with singleton(ref = softReference) {
441
ImageCacheImpl() // Uses soft references, cleared under memory pressure
442
}
443
444
bind<ThreadContext>() with singleton(ref = threadLocal) {
445
ThreadContextImpl() // One per thread
446
}
447
448
bind<TemporaryData>() with singleton(ref = weakReference) {
449
TemporaryDataImpl() // Automatically cleaned up
450
}
451
}
452
453
// Context translation for complex scenarios
454
val translatingDI = DI {
455
// Register multiple context translators
456
registerContextTranslator<HttpRequest, UserSession> { request ->
457
val session = sessionService.getSession(request.sessionToken)
458
UserSession(session.userId, session.id)
459
}
460
461
registerContextTranslator<UserSession, UserPermissions> { session ->
462
permissionService.getPermissions(session.userId)
463
}
464
465
registerContextFinder<DatabaseConnection> {
466
connectionPool.getConnection()
467
}
468
469
// Bindings that use translated contexts
470
contexted<UserSession>().apply {
471
bind<UserService>() with singleton { UserServiceImpl(diContext.value.userId) }
472
}
473
474
contexted<UserPermissions>().apply {
475
bind<AuthorizationService>() with singleton {
476
AuthorizationServiceImpl(diContext.value)
477
}
478
}
479
}
480
```