CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-kodein-di--kodein-di

KOtlin DEpendency INjection - A straightforward and yet very useful dependency retrieval container for Kotlin Multiplatform

Pending
Overview
Eval results
Files

binding-dsl.mddocs/

Binding DSL

Declarative syntax for binding types to their implementations with various creation patterns including singleton, provider, factory, instance, and multiton bindings.

Capabilities

Core Binding Methods

Generic binding methods that form the foundation of the DSL syntax for attaching types to their creation strategies.

/**
 * Core binding interface for type-to-implementation mappings
 */
interface DI.Builder {
    /**
     * Start binding a specific type with optional tag and override settings
     * @param type TypeToken representing the type to bind
     * @param tag Optional tag for disambiguation
     * @param overrides Whether this binding may override existing bindings
     * @return TypeBinder for completing the binding with 'with' clause
     */
    fun <T : Any> Bind(type: TypeToken<out T>, tag: Any? = null, overrides: Boolean? = null): TypeBinder<T>
    
    /**
     * Directly attach a pre-created binding to the container
     * @param tag Optional tag for the binding
     * @param overrides Whether this binding may override existing bindings
     * @param binding The binding implementation to attach
     */
    fun <T : Any> Bind(tag: Any? = null, overrides: Boolean? = null, binding: DIBinding<*, *, T>)
    
    /**
     * Create a constant binding with a specific tag
     * @param tag Required tag for the constant
     * @param overrides Whether this binding may override existing bindings
     * @return ConstantBinder for completing the binding
     */
    fun constant(tag: Any, overrides: Boolean? = null): ConstantBinder
    
    /**
     * Create a delegate binding that forwards to another binding
     * @param type TypeToken for the type being delegated
     * @param tag Optional tag for the delegation
     * @param overrides Whether this binding may override existing bindings
     * @return DelegateBinder for specifying the target binding
     */
    fun <T : Any> Delegate(
        type: TypeToken<out T>,
        tag: Any? = null,
        overrides: Boolean? = null
    ): DelegateBinder<T>
}

/**
 * Type binder for completing binding syntax with 'with' keyword
 */
interface DI.Builder.TypeBinder<T : Any> {
    /**
     * Complete the binding by attaching a specific binding implementation
     * @param binding The binding that creates instances of type T
     */
    infix fun <C : Any, A> with(binding: DIBinding<in C, in A, out T>)
}

/**
 * Constant binder for binding tagged constants
 */
interface DI.Builder.ConstantBinder {
    /**
     * Bind a constant value with specific type
     * @param valueType TypeToken for the constant's type
     * @param value The constant value to bind
     */
    fun <T : Any> With(valueType: TypeToken<out T>, value: T)
}

/**
 * Set binder for managing multiple bindings of the same type in a Set
 */
interface DI.Builder.SetBinder<T : Any> {
    /**
     * Add a binding to the set without adding it to the main container
     * @param createBinding Function that creates the binding to add
     */
    fun add(createBinding: () -> DIBinding<*, *, out T>)
    
    /**
     * Add a binding to both the set and the main container
     * @param tag Optional tag for the individual binding
     * @param overrides Whether this binding may override existing bindings
     * @param createBinding Function that creates the binding to add
     */
    fun bind(tag: Any? = null, overrides: Boolean? = null, createBinding: () -> DIBinding<*, *, out T>)
}

/**
 * Argument set binder for managing multiple factory bindings of the same type in a Set
 */
interface DI.Builder.ArgSetBinder<A : Any, T : Any> {
    /**
     * Add a factory binding to the set without adding it to the main container
     * @param createBinding Function that creates the factory binding to add
     */
    fun add(createBinding: () -> DIBinding<*, in A, out T>)
    
    /**
     * Add a factory binding to both the set and the main container
     * @param tag Optional tag for the individual binding
     * @param overrides Whether this binding may override existing bindings
     * @param createBinding Function that creates the factory binding to add
     */
    fun bind(
        tag: Any? = null,
        overrides: Boolean? = null,
        createBinding: () -> DIBinding<*, in A, out T>,
    )
}

Set Binding Methods

Methods for creating and managing set bindings that collect multiple implementations of the same interface.

/**
 * Create a new set binding and configure it with multiple bindings
 * @param tag Optional tag for the set binding
 * @param overrides Whether this set binding may override existing bindings
 * @param type TypeToken for the set element type
 * @param creator Configuration function for adding bindings to the set
 */
fun <T : Any> DI.Builder.BindInSet(
    tag: Any? = null,
    overrides: Boolean? = null,
    type: TypeToken<out T>,
    creator: SetBinder<T>.() -> Unit,
)

/**
 * Add bindings to an existing set binding
 * @param tag Optional tag for the set binding to add to
 * @param overrides Whether added bindings may override existing bindings
 * @param type TypeToken for the set element type
 * @param creator Configuration function for adding bindings to the set
 */
fun <T : Any> DI.Builder.InBindSet(
    tag: Any? = null,
    overrides: Boolean? = null,
    type: TypeToken<out T>,
    creator: SetBinder<T>.() -> Unit,
)

/**
 * Create a new argument set binding for factory collections
 * @param tag Optional tag for the set binding
 * @param overrides Whether this set binding may override existing bindings
 * @param argType TypeToken for the factory argument type
 * @param type TypeToken for the factory return type
 * @param creator Configuration function for adding factory bindings to the set
 */
fun <A : Any, T : Any> DI.Builder.BindInArgSet(
    tag: Any? = null,
    overrides: Boolean? = null,
    argType: TypeToken<in A>,
    type: TypeToken<out T>,
    creator: ArgSetBinder<A, T>.() -> Unit,
)

/**
 * Add factory bindings to an existing argument set binding
 * @param tag Optional tag for the set binding to add to
 * @param overrides Whether added bindings may override existing bindings
 * @param argType TypeToken for the factory argument type
 * @param type TypeToken for the factory return type
 * @param creator Configuration function for adding factory bindings to the set
 */
fun <A : Any, T : Any> DI.Builder.InBindArgSet(
    tag: Any? = null,
    overrides: Boolean? = null,
    argType: TypeToken<in A>,
    type: TypeToken<out T>,
    creator: ArgSetBinder<A, T>.() -> Unit,
)

/**
 * Add a single binding to an existing set binding
 * @param tag Optional tag for the set binding to add to
 * @param overrides Whether this binding may override existing bindings
 * @param binding The binding to add to the set
 */
fun <T : Any> DI.Builder.AddBindInSet(
    tag: Any? = null,
    overrides: Boolean? = null,
    binding: DIBinding<*, *, T>,
)

Constructor Injection Binding Methods

Convenience methods for binding types using their constructors with automatic dependency injection. These methods support up to 22 constructor parameters and automatically resolve dependencies for each parameter.

/**
 * Bind a singleton using constructor injection (0-22 parameters)
 * @param constructor Constructor function reference (e.g., ::MyClass)
 * @param tag Optional tag for the binding
 * @param overrides Whether this binding may override existing bindings
 * @param sync Whether the singleton creation should be synchronized
 */
fun <T : Any> DI.Builder.bindSingletonOf(
    crossinline constructor: () -> T,
    tag: Any? = null,
    overrides: Boolean? = null,
    sync: Boolean = true,
): Unit

fun <T : Any, P1> DI.Builder.bindSingletonOf(
    crossinline constructor: (P1) -> T,
    tag: Any? = null,
    overrides: Boolean? = null,
    sync: Boolean = true,
): Unit

fun <T : Any, P1, P2> DI.Builder.bindSingletonOf(
    crossinline constructor: (P1, P2) -> T,
    tag: Any? = null,
    overrides: Boolean? = null,
    sync: Boolean = true,
): Unit

fun <T : Any, P1, P2, P3> DI.Builder.bindSingletonOf(
    crossinline constructor: (P1, P2, P3) -> T,
    tag: Any? = null,
    overrides: Boolean? = null,
    sync: Boolean = true,
): Unit
// ... continues up to 22 parameters

/**
 * Bind a provider using constructor injection (0-22 parameters)
 * @param constructor Constructor function reference (e.g., ::MyClass)
 * @param tag Optional tag for the binding
 * @param overrides Whether this binding may override existing bindings
 */
fun <T : Any> DI.Builder.bindProviderOf(
    crossinline constructor: () -> T,
    tag: Any? = null,
    overrides: Boolean? = null,
): Unit

fun <T : Any, P1> DI.Builder.bindProviderOf(
    crossinline constructor: (P1) -> T,
    tag: Any? = null,
    overrides: Boolean? = null,
): Unit

fun <T : Any, P1, P2> DI.Builder.bindProviderOf(
    crossinline constructor: (P1, P2) -> T,
    tag: Any? = null,
    overrides: Boolean? = null,
): Unit

fun <T : Any, P1, P2, P3> DI.Builder.bindProviderOf(
    crossinline constructor: (P1, P2, P3) -> T,
    tag: Any? = null,
    overrides: Boolean? = null,
): Unit
// ... continues up to 22 parameters

/**
 * Bind an eager singleton using constructor injection (0-22 parameters)
 * @param constructor Constructor function reference (e.g., ::MyClass)
 * @param tag Optional tag for the binding
 * @param overrides Whether this binding may override existing bindings
 */
fun <T : Any> DI.Builder.bindEagerSingletonOf(
    crossinline constructor: () -> T,
    tag: Any? = null,
    overrides: Boolean? = null,
): Unit

fun <T : Any, P1> DI.Builder.bindEagerSingletonOf(
    crossinline constructor: (P1) -> T,
    tag: Any? = null,
    overrides: Boolean? = null,
): Unit
// ... continues up to 22 parameters

Usage Examples:

import org.kodein.di.*

class DatabaseConfig(val url: String, val maxConnections: Int)
class DatabaseService(private val config: DatabaseConfig)
class UserRepository(private val db: DatabaseService, private val logger: Logger)
class UserService(private val repository: UserRepository, private val validator: UserValidator)

val di = DI {
    // Bind configuration
    bindInstance<DatabaseConfig> { DatabaseConfig("jdbc:h2:mem:test", 10) }
    bindInstance<Logger> { ConsoleLogger() }
    
    // Use constructor injection - dependencies automatically resolved
    bindSingletonOf(::DatabaseService)      // Injects DatabaseConfig
    bindSingletonOf(::UserRepository)       // Injects DatabaseService and Logger
    bindProviderOf(::UserValidator)         // Creates new validator instances
    bindSingletonOf(::UserService)          // Injects UserRepository and UserValidator
}

Factory Bindings

Factory bindings create new instances each time they are requested, accepting an argument to customize the created instance.

/**
 * Factory binding that takes an argument and returns a new instance
 * @param C Context type for scoped bindings
 * @param A Argument type for the factory function
 * @param T Return type of the created instances
 */
class Factory<C : Any, A, T : Any> : DIBinding<C, A, T>

/**
 * Create a factory binding within a BindBuilder context
 * @param creator Function that creates instances from arguments
 * @return Factory binding instance
 */
fun <A, T : Any> DI.BindBuilder<*>.factory(
    creator: BindingDI<*>.(A) -> T
): Factory<*, A, T>

/**
 * Directly bind a factory with convenience method
 * @param tag Optional tag for the binding
 * @param overrides Whether this binding may override existing bindings
 * @param creator Function that creates instances from arguments
 */
fun <A, T : Any> DI.Builder.bindFactory(
    tag: Any? = null,
    overrides: Boolean? = null,
    creator: DirectDI.(A) -> T
)

Provider Bindings

Provider bindings create new instances each time they are requested, taking no arguments (no-argument factories).

/**
 * Provider binding that creates new instances without arguments
 * @param C Context type for scoped bindings
 * @param T Return type of the created instances
 */
class Provider<C : Any, T : Any> : DIBinding<C, Unit, T>

/**
 * Create a provider binding within a BindBuilder context
 * @param creator Function that creates instances
 * @return Provider binding instance
 */
fun <T : Any> DI.BindBuilder<*>.provider(
    creator: NoArgBindingDI<*>.() -> T
): Provider<*, T>

/**
 * Directly bind a provider with convenience method
 * @param tag Optional tag for the binding
 * @param overrides Whether this binding may override existing bindings
 * @param creator Function that creates instances
 */
fun <T : Any> DI.Builder.bindProvider(
    tag: Any? = null,
    overrides: Boolean? = null,
    creator: DirectDI.() -> T
)

/**
 * Bind a provider using constructor injection (up to 22 parameters)
 * @param constructor Constructor function reference
 * @param tag Optional tag for the binding
 * @param overrides Whether this binding may override existing bindings
 */
fun <T : Any> DI.Builder.bindProviderOf(
    constructor: () -> T,
    tag: Any? = null,
    overrides: Boolean? = null
)

fun <P1, T : Any> DI.Builder.bindProviderOf(
    constructor: (P1) -> T,
    tag: Any? = null,
    overrides: Boolean? = null
)

// ... up to 22 parameter overloads

Singleton Bindings

Singleton bindings create one instance per scope and reuse it for all subsequent requests within that scope.

/**
 * Singleton binding that creates one instance per scope
 * @param C Context type for the scoped singleton
 * @param T Return type of the singleton instance
 */
class Singleton<C : Any, T : Any> : DIBinding<C, Unit, T>

/**
 * Create a singleton binding within a scoped BindBuilder context
 * @param ref Reference type for instance storage (default, thread-local, etc.)
 * @param sync Whether to synchronize access to the singleton
 * @param creator Function that creates the singleton instance
 * @return Singleton binding instance
 */
fun <T : Any> DI.BindBuilder.WithScope<*>.singleton(
    ref: RefMaker = SingletonReference,
    sync: Boolean = true,
    creator: NoArgBindingDI<*>.() -> T
): Singleton<*, T>

/**
 * Directly bind a singleton with convenience method
 * @param tag Optional tag for the binding
 * @param overrides Whether this binding may override existing bindings
 * @param sync Whether to synchronize access to the singleton
 * @param creator Function that creates the singleton instance
 */
fun <T : Any> DI.Builder.bindSingleton(
    tag: Any? = null,
    overrides: Boolean? = null,
    sync: Boolean = true,
    creator: DirectDI.() -> T
)

/**
 * Bind a singleton using constructor injection (up to 22 parameters)
 * @param constructor Constructor function reference
 * @param tag Optional tag for the binding
 * @param overrides Whether this binding may override existing bindings
 * @param sync Whether to synchronize access to the singleton
 */
fun <T : Any> DI.Builder.bindSingletonOf(
    constructor: () -> T,
    tag: Any? = null,
    overrides: Boolean? = null,
    sync: Boolean = true
)

// ... up to 22 parameter overloads

Eager Singleton Bindings

Eager singleton bindings create their instance immediately when the DI container is built, not on first access.

/**
 * Eager singleton that is created when the DI container is built
 * @param T Return type of the eager singleton instance
 */
class EagerSingleton<T : Any> : DIBinding<Any, Unit, T>

/**
 * Create an eager singleton binding
 * @param creator Function that creates the instance immediately
 * @return EagerSingleton binding instance
 */
fun <T : Any> DI.Builder.eagerSingleton(
    creator: DirectDI.() -> T
): EagerSingleton<T>

/**
 * Directly bind an eager singleton with convenience method
 * @param tag Optional tag for the binding
 * @param overrides Whether this binding may override existing bindings
 * @param creator Function that creates the instance immediately
 */
fun <T : Any> DI.Builder.bindEagerSingleton(
    tag: Any? = null,
    overrides: Boolean? = null,
    creator: DirectDI.() -> T
)

Instance Bindings

Instance bindings store pre-created objects in the DI container for later retrieval.

/**
 * Instance binding that stores a pre-created object
 * @param T Type of the stored instance
 */
class InstanceBinding<T : Any> : DIBinding<Any, Unit, T>

/**
 * Create an instance binding from a pre-created object
 * @param instance The pre-created instance to store
 * @return InstanceBinding that returns the stored instance
 */
fun <T : Any> DI.Builder.instance(instance: T): InstanceBinding<T>

/**
 * Directly bind an instance with convenience method
 * @param tag Optional tag for the binding
 * @param overrides Whether this binding may override existing bindings
 * @param creator Function that provides the instance to store
 */
fun <T : Any> DI.Builder.bindInstance(
    tag: Any? = null,
    overrides: Boolean? = null,
    creator: () -> T
)

/**
 * Bind a constant value (alias for bindInstance)
 * @param tag Optional tag for the binding
 * @param overrides Whether this binding may override existing bindings
 * @param creator Function that provides the constant value
 */
fun <T : Any> DI.Builder.bindConstant(
    tag: Any? = null,
    overrides: Boolean? = null,
    creator: () -> T
)

Multiton Bindings

Multiton bindings create one instance per unique argument value, combining aspects of factory and singleton patterns.

/**
 * Multiton binding that creates one instance per unique argument
 * @param C Context type for scoped multitons
 * @param A Argument type used as the multiton key
 * @param T Return type of the multiton instances
 */
class Multiton<C : Any, A, T : Any> : DIBinding<C, A, T>

/**
 * Create a multiton binding within a scoped BindBuilder context
 * @param ref Reference type for instance storage
 * @param sync Whether to synchronize access to multiton instances
 * @param creator Function that creates instances for each unique argument
 * @return Multiton binding instance
 */
fun <A, T : Any> DI.BindBuilder.WithScope<*>.multiton(
    ref: RefMaker = MultitonReference,
    sync: Boolean = true,
    creator: BindingDI<*>.(A) -> T
): Multiton<*, A, T>

/**
 * Directly bind a multiton with convenience method
 * @param tag Optional tag for the binding
 * @param overrides Whether this binding may override existing bindings
 * @param sync Whether to synchronize access to multiton instances
 * @param creator Function that creates instances for each unique argument
 */
fun <A, T : Any> DI.Builder.bindMultiton(
    tag: Any? = null,
    overrides: Boolean? = null,
    sync: Boolean = true,
    creator: DirectDI.(A) -> T
)

Set Bindings

Set bindings allow multiple bindings for the same type, collecting them into a Set for retrieval.

/**
 * Create a set binding and add elements to it
 * @param tag Optional tag for the set binding
 * @param overrides Whether this binding may override existing bindings
 * @param type TypeToken for the set element type
 * @param creator Block that adds bindings to the set
 */
fun <T : Any> DI.Builder.BindInSet(
    tag: Any? = null,
    overrides: Boolean? = null,
    type: TypeToken<out T>,
    creator: SetBinder<T>.() -> Unit
)

/**
 * Add elements to an existing set binding
 * @param tag Optional tag for the set binding
 * @param overrides Whether this addition may override existing elements
 * @param type TypeToken for the set element type
 * @param creator Block that adds bindings to the set
 */
fun <T : Any> DI.Builder.InBindSet(
    tag: Any? = null,
    overrides: Boolean? = null,
    type: TypeToken<out T>,
    creator: SetBinder<T>.() -> Unit
)

/**
 * Add a single binding to a set
 * @param tag Optional tag for the set binding
 * @param overrides Whether this addition may override existing elements
 * @param binding The binding to add to the set
 */
fun <T : Any> DI.Builder.AddBindInSet(
    tag: Any? = null,
    overrides: Boolean? = null,
    binding: DIBinding<*, *, T>
)

/**
 * Set binder for adding multiple bindings to a set
 */
interface DI.Builder.SetBinder<T : Any> {
    /**
     * Add a binding to the set
     * @param createBinding Function that creates the binding to add
     */
    fun add(createBinding: () -> DIBinding<*, *, out T>)
    
    /**
     * Add a binding to both the set and the DI container
     * @param tag Optional tag for the binding
     * @param overrides Whether this binding may override existing bindings
     * @param createBinding Function that creates the binding to add
     */
    fun bind(
        tag: Any? = null,
        overrides: Boolean? = null,
        createBinding: () -> DIBinding<*, *, out T>
    )
}

Usage Examples:

val di = DI {
    // Factory binding - new instance each time with argument
    bind<UserRepository>() with factory { userId: String -> 
        UserRepositoryImpl(userId, instance()) 
    }
    
    // Provider binding - new instance each time, no arguments
    bind<EmailService>() with provider { 
        EmailServiceImpl(instance(), instance()) 
    }
    
    // Singleton binding - one instance per container
    bind<Database>() with singleton { 
        DatabaseImpl(constant("connectionString")) 
    }
    
    // Eager singleton - created immediately when DI is built
    bind<ConfigService>() with eagerSingleton { 
        ConfigServiceImpl().also { it.initialize() } 
    }
    
    // Instance binding - pre-created object
    bind<AppConfig>() with instance(AppConfig(debug = true))
    
    // Multiton binding - one instance per unique argument
    bind<Cache>() with multiton { cacheKey: String ->
        InMemoryCache(cacheKey, instance())
    }
    
    // Constant binding with tag
    constant("apiUrl") with "https://api.example.com"
    constant("timeout") with 30_000L
    
    // Set binding - multiple implementations
    bindInSet<Plugin> {
        add { provider { LoggingPlugin() } }
        add { provider { MetricsPlugin() } }
        add { provider { SecurityPlugin() } }
    }
    
    // Constructor injection with bindProviderOf
    bindProviderOf(::UserServiceImpl) // Automatically injects constructor parameters
    bindSingletonOf(::DatabaseConnectionPool)
    
    // Using overrides
    bind<Logger>(overrides = true) with singleton {
        if (constant<Boolean>("debug")) ConsoleLogger() else FileLogger()
    }
}

// Usage in classes
class UserController : DIAware {
    override val di = di
    
    private val userRepo: UserRepository by factory()
    private val emailService: EmailService by provider()
    private val database: Database by instance()
    private val plugins: Set<Plugin> by instance()
    
    fun createUser(userId: String) {
        val repo = userRepo(userId) // Factory with argument
        val email = emailService() // Provider creates new instance
        // database is singleton, same instance each time
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-org-kodein-di--kodein-di

docs

advanced-features.md

binding-dsl.md

container-configuration.md

direct-access.md

index.md

lazy-property-delegation.md

scoping-and-context.md

tile.json