KOtlin DEpendency INjection - A straightforward and yet very useful dependency retrieval container for Kotlin Multiplatform
—
Core DI container creation and configuration functionality for managing dependency injection containers, modules, and lifecycle callbacks.
Creates and configures the main dependency injection container with customizable settings and initialization blocks.
/**
* Main dependency injection container interface
*/
interface DI : DIAware {
val container: DIContainer
companion object {
/**
* Creates a DI instance with configuration block
* @param allowSilentOverride Whether to allow implicit binding overrides
* @param init Configuration block for defining bindings
* @return Configured DI container
*/
operator fun invoke(
allowSilentOverride: Boolean = false,
init: MainBuilder.() -> Unit
): DI
/**
* Creates a lazily initialized DI instance
* @param allowSilentOverride Whether to allow implicit binding overrides
* @param init Configuration block for defining bindings
* @return LazyDI wrapper that initializes on first access
*/
fun lazy(
allowSilentOverride: Boolean = false,
init: MainBuilder.() -> Unit
): LazyDI
/**
* Creates a direct DI instance for immediate access
* @param allowSilentOverride Whether to allow implicit binding overrides
* @param init Configuration block for defining bindings
* @return DirectDI instance for immediate dependency access
*/
fun direct(
allowSilentOverride: Boolean = false,
init: MainBuilder.() -> Unit
): DirectDI
/**
* Creates DI with delayed callback execution (internal API)
* @param allowSilentOverride Whether to allow implicit binding overrides
* @param init Configuration block for defining bindings
* @return Pair of DI instance and callback function
*/
fun withDelayedCallbacks(
allowSilentOverride: Boolean = false,
init: MainBuilder.() -> Unit
): Pair<DI, () -> Unit>
/**
* Creates DI from a list of modules
* @param modules List of modules to import
* @return DI instance with all module bindings
*/
fun from(modules: List<Module>): DI
/** Global default for full error descriptions */
var defaultFullDescriptionOnError: Boolean
/** Global default for full container tree in errors */
var defaultFullContainerTreeOnError: Boolean
}
}Deferred DI initialization patterns for cases where the container cannot be immediately created.
/**
* Lazily initialized DI container
* @param f Function that creates the DI instance when needed
*/
class LazyDI(f: () -> DI) : DI {
// Delegates all DI operations to the lazily created instance
}
/**
* Manually initialized DI container for late binding scenarios
*/
class LateInitDI : DI {
/**
* The base DI instance (must be set before use)
*/
lateinit var baseDI: DI
}Reusable configuration modules for organizing and sharing binding definitions across DI containers.
/**
* Reusable configuration module for dependency bindings
* @param allowSilentOverride Whether this module allows implicit overrides
* @param prefix String prefix for all bindings in this module
* @param init Configuration block defining the module's bindings
*/
data class DI.Module(
val allowSilentOverride: Boolean = false,
val prefix: String = "",
val init: Builder.() -> Unit
) {
val name: String // Module name (required)
/**
* Named module constructor
* @param name Unique name for this module
* @param allowSilentOverride Whether this module allows implicit overrides
* @param prefix String prefix for all bindings in this module
* @param init Configuration block defining the module's bindings
*/
constructor(
name: String,
allowSilentOverride: Boolean = false,
prefix: String = "",
init: Builder.() -> Unit
)
/**
* Property delegate support for automatic naming
*/
operator fun getValue(thisRef: Any?, property: KProperty<*>): Module
}DSL interfaces for configuring DI containers with bindings, modules, and lifecycle callbacks.
/**
* Main builder interface for DI configuration
*/
interface DI.Builder : BindBuilder<Any>, BindBuilder.WithScope<Any> {
val containerBuilder: DIContainer.Builder
/**
* Import a module into this DI configuration
* @param module The module to import
* @param allowOverride Whether to allow binding overrides
*/
fun import(module: Module, allowOverride: Boolean = false)
/**
* Import multiple modules into this DI configuration
* @param modules Modules to import
* @param allowOverride Whether to allow binding overrides
*/
fun importAll(vararg modules: Module, allowOverride: Boolean = false)
fun importAll(modules: Iterable<Module>, allowOverride: Boolean = false)
/**
* Import a module only once (checked by name)
* @param module The module to import once
* @param allowOverride Whether to allow binding overrides
*/
fun importOnce(module: Module, allowOverride: Boolean = false)
/**
* Register a callback to be called when DI is ready
* @param cb Callback function receiving DirectDI instance
*/
fun onReady(cb: DirectDI.() -> Unit)
/**
* Register a context translator for scope management
* @param translator Context translator instance
*/
fun RegisterContextTranslator(translator: ContextTranslator<*, *>)
}
/**
* Extended builder for main DI creation with additional configuration
*/
interface DI.MainBuilder : Builder {
/** Whether to include qualified names in error messages */
var fullDescriptionOnError: Boolean
/** Whether to include full container tree in NotFoundException */
var fullContainerTreeOnError: Boolean
/** List of external sources for fallback binding resolution */
val externalSources: MutableList<ExternalSource>
/**
* Extend this DI with another DI container
* @param di The DI container to extend from
* @param allowOverride Whether to allow binding overrides
* @param copy Copy specification for which bindings to copy
*/
fun extend(di: DI, allowOverride: Boolean = false, copy: Copy = Copy.NonCached)
fun extend(directDI: DirectDI, allowOverride: Boolean = false, copy: Copy = Copy.NonCached)
}Advanced container composition and extension patterns for building complex DI hierarchies.
/**
* Copy specification for container extension
*/
sealed class Copy {
/** Copy no bindings (default) */
object None : Copy()
/** Copy all bindings */
object All : Copy()
/** Copy only non-cached bindings (providers, factories) */
object NonCached : Copy()
}Usage Examples:
// Basic DI container creation
val di = DI {
bind<DataService>() with singleton { DataServiceImpl() }
bind<UserRepository>() with provider { UserRepositoryImpl(instance()) }
}
// Module definition and usage
val dataModule = DI.Module("data") {
bind<Database>() with singleton { SQLiteDatabase() }
bind<UserDao>() with provider { UserDaoImpl(instance()) }
}
val serviceModule = DI.Module("services") {
bind<UserService>() with provider { UserServiceImpl(instance()) }
}
val appDI = DI {
import(dataModule)
import(serviceModule)
// Override specific bindings
bind<Database>(overrides = true) with singleton {
if (BuildConfig.DEBUG) MockDatabase() else SQLiteDatabase()
}
}
// Lazy DI initialization
val lazyDI = DI.lazy {
bind<ConfigService>() with singleton { ConfigServiceImpl() }
onReady {
// Initialization code when DI is ready
println("DI container initialized")
}
}
// Container extension
val extendedDI = DI {
extend(appDI, allowOverride = true, copy = Copy.NonCached)
// Add additional bindings
bind<TestService>() with singleton { TestServiceImpl() }
}
// Late initialization pattern
class Application : DIAware {
private val lateInitDI = LateInitDI()
override val di: DI = lateInitDI
fun initialize() {
lateInitDI.baseDI = DI {
bind<AppService>() with singleton { AppServiceImpl() }
}
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-kodein-di--kodein-di