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

direct-access.mddocs/

Direct Access

Immediate dependency retrieval without property delegation for cases requiring direct access to dependencies, providing synchronous resolution and explicit control over dependency lifecycle.

Capabilities

DirectDI Interface

Core interface for immediate dependency access without lazy initialization or property delegation overhead.

/**
 * Base interface for classes that use direct dependency injection
 */
interface DirectDIAware {
    /** Reference to the DirectDI instance */
    val directDI: DirectDI
}

/**
 * Base functionality for direct dependency access
 */
interface DirectDIBase : DirectDIAware {
    /** The underlying container for dependency resolution */
    val container: DIContainer
    
    /** Get a regular lazy DI instance from this DirectDI */
    val lazy: DI
    
    /** Alias for lazy property */
    val di: DI
    
    /**
     * Create a new DirectDI with different context
     * @param context New context for scoped dependency resolution
     * @return DirectDI instance with the specified context
     */
    fun On(context: DIContext<*>): DirectDI
}

/**
 * Platform-specific DirectDI interface for immediate dependency access
 * Acts like a DI object but returns values instead of property delegates
 */
expect interface DirectDI : DirectDIBase

Direct Instance Retrieval

Immediate retrieval of dependency instances without lazy initialization or caching overhead.

/**
 * Get an instance of type T immediately
 * @param type TypeToken representing the type to retrieve
 * @param tag Optional tag for disambiguation
 * @return Instance of type T
 * @throws DI.NotFoundException if no binding is found
 * @throws DI.DependencyLoopException if circular dependency is detected
 */
fun <T : Any> DirectDI.Instance(type: TypeToken<T>, tag: Any? = null): T

/**
 * Get an instance with factory argument immediately
 * @param argType TypeToken for the argument type
 * @param type TypeToken for the return type
 * @param tag Optional tag for disambiguation
 * @param arg Argument value for the factory
 * @return Instance of type T created with the argument
 * @throws DI.NotFoundException if no binding is found
 * @throws DI.DependencyLoopException if circular dependency is detected
 */
fun <A, T : Any> DirectDI.Instance(
    argType: TypeToken<in A>,
    type: TypeToken<T>,
    tag: Any? = null,
    arg: A
): T

/**
 * Get an instance or null if not found
 * @param type TypeToken representing the type to retrieve
 * @param tag Optional tag for disambiguation
 * @return Instance of type T or null if no binding found
 * @throws DI.DependencyLoopException if circular dependency is detected
 */
fun <T : Any> DirectDI.InstanceOrNull(type: TypeToken<T>, tag: Any? = null): T?

/**
 * Get an instance with factory argument or null if not found
 * @param argType TypeToken for the argument type
 * @param type TypeToken for the return type
 * @param tag Optional tag for disambiguation
 * @param arg Argument value for the factory
 * @return Instance of type T or null if no binding found
 * @throws DI.DependencyLoopException if circular dependency is detected
 */
fun <A, T : Any> DirectDI.InstanceOrNull(
    argType: TypeToken<in A>,
    type: TypeToken<T>,
    tag: Any? = null,
    arg: A
): T?

Direct Provider Retrieval

Immediate retrieval of provider functions that create new instances when called.

/**
 * Get a provider function immediately
 * @param type TypeToken representing the type to provide
 * @param tag Optional tag for disambiguation
 * @return Provider function () -> T
 * @throws DI.NotFoundException if no binding is found
 * @throws DI.DependencyLoopException if circular dependency is detected
 */
fun <T : Any> DirectDI.Provider(type: TypeToken<T>, tag: Any? = null): () -> T

/**
 * Get a provider curried from a factory with argument
 * @param argType TypeToken for the argument type
 * @param type TypeToken for the return type
 * @param tag Optional tag for disambiguation
 * @param arg Function providing the argument for currying
 * @return Provider function () -> T
 * @throws DI.NotFoundException if no binding is found
 * @throws DI.DependencyLoopException if circular dependency is detected
 */
fun <A, T : Any> DirectDI.Provider(
    argType: TypeToken<in A>,
    type: TypeToken<T>,
    tag: Any? = null,
    arg: () -> A
): () -> T

/**
 * Get a provider function or null if not found
 * @param type TypeToken representing the type to provide
 * @param tag Optional tag for disambiguation
 * @return Provider function (() -> T)? or null if no binding found
 * @throws DI.DependencyLoopException if circular dependency is detected
 */
fun <T : Any> DirectDI.ProviderOrNull(type: TypeToken<T>, tag: Any? = null): (() -> T)?

/**
 * Get a provider curried from a factory or null if not found
 * @param argType TypeToken for the argument type
 * @param type TypeToken for the return type
 * @param tag Optional tag for disambiguation
 * @param arg Function providing the argument for currying
 * @return Provider function (() -> T)? or null if no binding found
 * @throws DI.DependencyLoopException if circular dependency is detected
 */
fun <A, T : Any> DirectDI.ProviderOrNull(
    argType: TypeToken<in A>,
    type: TypeToken<T>,
    tag: Any? = null,
    arg: () -> A
): (() -> T)?

Direct Factory Retrieval

Immediate retrieval of factory functions that accept arguments and create instances.

/**
 * Get a factory function immediately
 * @param argType TypeToken for the argument type
 * @param type TypeToken for the return type
 * @param tag Optional tag for disambiguation
 * @return Factory function (A) -> T
 * @throws DI.NotFoundException if no binding is found
 * @throws DI.DependencyLoopException if circular dependency is detected
 */
fun <A, T : Any> DirectDI.Factory(
    argType: TypeToken<in A>,
    type: TypeToken<T>,
    tag: Any? = null
): (A) -> T

/**
 * Get a factory function or null if not found
 * @param argType TypeToken for the argument type
 * @param type TypeToken for the return type
 * @param tag Optional tag for disambiguation
 * @return Factory function ((A) -> T)? or null if no binding found
 * @throws DI.DependencyLoopException if circular dependency is detected
 */
fun <A, T : Any> DirectDI.FactoryOrNull(
    argType: TypeToken<in A>,
    type: TypeToken<T>,
    tag: Any? = null
): ((A) -> T)?

Reified Extension Methods

Type-safe extension methods for DirectDIAware that eliminate the need for explicit TypeToken parameters by using reified generics.

/**
 * Get a factory function for type T with argument type A
 * @param tag Optional tag for disambiguation
 * @return Factory function that takes A and returns T
 * @throws DI.NotFoundException if no binding is found
 */
inline fun <reified A : Any, reified T : Any> DirectDIAware.factory(tag: Any? = null): (A) -> T

/**
 * Get a factory function for type T with argument type A, returning null if not found
 * @param tag Optional tag for disambiguation
 * @return Factory function or null if no binding is found
 */
inline fun <reified A : Any, reified T : Any> DirectDIAware.factoryOrNull(tag: Any? = null): ((A) -> T)?

/**
 * Get a provider function for type T
 * @param tag Optional tag for disambiguation
 * @return Provider function that returns T
 * @throws DI.NotFoundException if no binding is found
 */
inline fun <reified T : Any> DirectDIAware.provider(tag: Any? = null): () -> T

/**
 * Get a provider function for type T with curried argument
 * @param tag Optional tag for disambiguation
 * @param arg Argument value to curry into the provider
 * @return Provider function that returns T
 */
inline fun <reified A : Any, reified T : Any> DirectDIAware.provider(tag: Any? = null, arg: A): () -> T

/**
 * Get a provider function for type T with typed argument
 * @param tag Optional tag for disambiguation
 * @param arg Typed argument value to curry into the provider
 * @return Provider function that returns T
 */
inline fun <A, reified T : Any> DirectDIAware.provider(tag: Any? = null, arg: Typed<A>): () -> T

/**
 * Get a provider function for type T with lazy argument
 * @param tag Optional tag for disambiguation
 * @param fArg Function that provides the argument value
 * @return Provider function that returns T
 */
inline fun <reified A : Any, reified T : Any> DirectDIAware.provider(tag: Any? = null, noinline fArg: () -> A): () -> T

/**
 * Get a provider function for type T, returning null if not found
 * @param tag Optional tag for disambiguation
 * @return Provider function or null if no binding is found
 */
inline fun <reified T : Any> DirectDIAware.providerOrNull(tag: Any? = null): (() -> T)?

/**
 * Get a provider function for type T with curried argument, returning null if not found
 * @param tag Optional tag for disambiguation
 * @param arg Argument value to curry into the provider
 * @return Provider function or null if no binding is found
 */
inline fun <reified A : Any, reified T : Any> DirectDIAware.providerOrNull(tag: Any? = null, arg: A): (() -> T)?

/**
 * Get a provider function for type T with typed argument, returning null if not found
 * @param tag Optional tag for disambiguation  
 * @param arg Typed argument value to curry into the provider
 * @return Provider function or null if no binding is found
 */
inline fun <A, reified T : Any> DirectDIAware.providerOrNull(tag: Any? = null, arg: Typed<A>): (() -> T)?

/**
 * Get a provider function for type T with lazy argument, returning null if not found
 * @param tag Optional tag for disambiguation
 * @param fArg Function that provides the argument value
 * @return Provider function or null if no binding is found
 */
inline fun <reified A : Any, reified T : Any> DirectDIAware.providerOrNull(tag: Any? = null, noinline fArg: () -> A): (() -> T)?

/**
 * Get an instance of type T immediately
 * @param tag Optional tag for disambiguation
 * @return Instance of type T
 * @throws DI.NotFoundException if no binding is found
 */
inline fun <reified T : Any> DirectDIAware.instance(tag: Any? = null): T

/**
 * Get an instance of type T with argument
 * @param tag Optional tag for disambiguation
 * @param arg Argument value for factory-based bindings
 * @return Instance of type T
 */
inline fun <reified A : Any, reified T : Any> DirectDIAware.instance(tag: Any? = null, arg: A): T

/**
 * Get an instance of type T with typed argument
 * @param tag Optional tag for disambiguation
 * @param arg Typed argument value for factory-based bindings
 * @return Instance of type T
 */
inline fun <A, reified T : Any> DirectDIAware.instance(tag: Any? = null, arg: Typed<A>): T

/**
 * Get an instance of type T, returning null if not found
 * @param tag Optional tag for disambiguation
 * @return Instance of type T or null if no binding is found
 */
inline fun <reified T : Any> DirectDIAware.instanceOrNull(tag: Any? = null): T?

/**
 * Get an instance of type T with argument, returning null if not found
 * @param tag Optional tag for disambiguation
 * @param arg Argument value for factory-based bindings
 * @return Instance of type T or null if no binding is found
 */
inline fun <reified A : Any, reified T : Any> DirectDIAware.instanceOrNull(tag: Any? = null, arg: A): T?

/**
 * Get an instance of type T with typed argument, returning null if not found
 * @param tag Optional tag for disambiguation
 * @param arg Typed argument value for factory-based bindings
 * @return Instance of type T or null if no binding is found
 */
inline fun <A, reified T : Any> DirectDIAware.instanceOrNull(tag: Any? = null, arg: Typed<A>): T?

/**
 * Create a DirectDI with typed context
 * @param context Context value for scoped dependencies
 * @return DirectDI instance with the specified context
 */
inline fun <reified C : Any> DirectDIAware.on(context: C): DirectDI

Constructor Injection (New Operator)

Advanced dependency injection for constructor parameters with automatic parameter resolution.

/**
 * Create a new instance using constructor injection (no parameters)
 * @param constructor Constructor function reference
 * @return New instance with injected dependencies
 */
fun <T> DirectDIAware.new(constructor: () -> T): T

/**
 * Create a new instance using constructor injection (1 parameter)
 * @param constructor Constructor function reference
 * @return New instance with injected dependencies
 */
fun <P1, T> DirectDIAware.new(constructor: (P1) -> T): T

/**
 * Create a new instance using constructor injection (2 parameters)
 * @param constructor Constructor function reference
 * @return New instance with injected dependencies
 */
fun <P1, P2, T> DirectDIAware.new(constructor: (P1, P2) -> T): T

// ... up to 22 parameter overloads

/**
 * Create a new instance within a DirectDI context
 * @param creator Function that creates the instance using DirectDI
 * @return Created instance
 */
inline fun <T> DirectDIAware.newInstance(creator: DirectDI.() -> T): T

Context Management

Direct context switching and scoped dependency access for fine-grained control over dependency resolution contexts.

/**
 * Create a DirectDI with typed context
 * @param context Context value for scoped dependencies
 * @return DirectDI instance with the specified context
 */
fun <C : Any> DirectDIAware.on(context: C): DirectDI

/**
 * Access the lazy DI interface from DirectDIAware
 */
val DirectDIAware.lazy: DI

Platform-Specific Extensions

Platform-specific DirectDI implementations with optimized method signatures and additional multi-binding support on JVM platform.

/**
 * JVM-specific DirectDI methods for multi-binding support
 * These methods are only available on the JVM platform
 */

/**
 * Gets all factories that can return a T for the given argument type, return type and tag
 * @param argType TypeToken for the argument type
 * @param type TypeToken for the return type
 * @param tag Optional tag for disambiguation  
 * @return List of all matching factory functions
 */
fun <A, T : Any> DirectDI.AllFactories(argType: TypeToken<in A>, type: TypeToken<T>, tag: Any? = null): List<(A) -> T>

/**
 * Gets all providers that can return a T for the given type and tag
 * @param type TypeToken for the return type
 * @param tag Optional tag for disambiguation
 * @return List of all matching provider functions
 */
fun <T : Any> DirectDI.AllProviders(type: TypeToken<T>, tag: Any? = null): List<() -> T>

/**
 * Gets all providers that can return a T for the given type and tag, curried from factories
 * @param argType TypeToken for the factory argument type
 * @param type TypeToken for the return type
 * @param tag Optional tag for disambiguation
 * @param arg Function providing the argument value
 * @return List of all matching provider functions with curried arguments
 */
fun <A, T : Any> DirectDI.AllProviders(argType: TypeToken<in A>, type: TypeToken<T>, tag: Any? = null, arg: () -> A): List<() -> T>

/**
 * Gets all instances that can return a T for the given type and tag
 * @param type TypeToken for the return type  
 * @param tag Optional tag for disambiguation
 * @return List of all matching instances
 */
fun <T : Any> DirectDI.AllInstances(type: TypeToken<T>, tag: Any? = null): List<T>

/**
 * Gets all instances that can return a T for the given type and tag, from factories
 * @param argType TypeToken for the factory argument type
 * @param type TypeToken for the return type
 * @param tag Optional tag for disambiguation
 * @param arg Argument value for the factories
 * @return List of all matching instances created with the argument
 */
fun <A, T : Any> DirectDI.AllInstances(argType: TypeToken<in A>, type: TypeToken<T>, tag: Any? = null, arg: A): List<T>

/**
 * Reified convenience methods for JVM multi-binding support
 */
inline fun <reified A : Any, reified T : Any> DirectDI.allFactories(tag: Any? = null): List<(A) -> T>
inline fun <reified T : Any> DirectDI.allProviders(tag: Any? = null): List<() -> T>
inline fun <reified A : Any, reified T : Any> DirectDI.allProviders(tag: Any? = null, noinline arg: () -> A): List<() -> T>
inline fun <reified T : Any> DirectDI.allInstances(tag: Any? = null): List<T>
inline fun <reified A : Any, reified T : Any> DirectDI.allInstances(tag: Any? = null, arg: A): List<T>
    // and integration with Java reflection
}

// JavaScript-specific DirectDI implementation  
// Located in src/jsBasedMain/kotlin/org/kodein/di/DirectDIJS.kt
actual interface DirectDI : DirectDIBase {
    // JS-optimized implementations for browser and Node.js environments
}

Usage Examples:

// Basic DirectDI usage
class UserService(private val directDI: DirectDI) : DirectDIAware {
    override val directDI = directDI
    
    fun createUser(userData: UserData): User {
        // Direct instance retrieval - immediate access
        val repository = directDI.instance<UserRepository>()
        val validator = directDI.instance<UserValidator>()
        val emailService = directDI.instance<EmailService>("smtp")
        
        // Factory with argument - immediate factory call
        val logger = directDI.factory<String, Logger>()(this::class.simpleName!!)
        
        // Provider for new instances each time
        val sessionProvider = directDI.provider<UserSession>()
        
        val user = User(userData)
        repository.save(user)
        emailService.sendWelcome(user)
        logger.info("User created: ${user.id}")
        
        return user
    }
    
    fun processUsers(userIds: List<String>) {
        // Get factory once, use multiple times
        val userFactory = directDI.factory<String, UserProcessor>()
        
        userIds.forEach { userId ->
            val processor = userFactory(userId) // Create processor for each user
            processor.process()
        }
    }
}

// Constructor injection with new operator
class OrderService : DirectDIAware {
    override val directDI: DirectDI = DI.direct {
        bind<PaymentProcessor>() with singleton { StripePaymentProcessor() }
        bind<InventoryService>() with singleton { InventoryServiceImpl() }
        bind<EmailService>() with provider { EmailServiceImpl() }
        bind<Logger>() with factory { name: String -> LoggerFactory.create(name) }
    }
    
    fun processOrder(orderData: OrderData) {
        // Automatic constructor injection - finds dependencies automatically
        val orderValidator = new(::OrderValidator) // Injects whatever OrderValidator needs
        val paymentHandler = new(::PaymentHandler) // Injects PaymentProcessor, Logger, etc.
        val fulfillmentService = new(::FulfillmentService) // Injects InventoryService, EmailService
        
        orderValidator.validate(orderData)
        val payment = paymentHandler.process(orderData.payment)
        fulfillmentService.fulfill(orderData, payment)
    }
}

// Context switching for scoped dependencies
class RequestHandler : DirectDIAware {
    override val directDI: DirectDI = appDirectDI
    
    fun handleRequest(request: HttpRequest) {
        val requestContext = RequestContext(request.sessionId, request.userId)
        
        // Switch to request-scoped context
        val scopedDI = directDI.on(requestContext)
        
        // These will use the request context for scoped singletons
        val userSession = scopedDI.instance<UserSession>() // Scoped to this request
        val requestLogger = scopedDI.instance<Logger>("request") // Request-specific logger
        val permissions = scopedDI.instance<UserPermissions>() // Cached per request
        
        // Process request with scoped dependencies
        processWithContext(userSession, requestLogger, permissions, request)
    }
}

// Mixing direct and lazy access patterns
class HybridService : DIAware, DirectDIAware {
    override val di: DI = appDI
    override val directDI: DirectDI = appDI.direct
    
    // Some dependencies as lazy properties (rarely accessed)
    private val configService: ConfigService by instance()
    private val metricsCollector: MetricsCollector by instance()
    
    fun processData(data: List<DataItem>) {
        // Direct access for per-operation dependencies
        val processor = directDI.instance<DataProcessor>()
        val validator = directDI.instance<DataValidator>()
        
        // Lazy properties accessed only when needed
        val config = configService.getProcessingConfig()
        
        data.forEach { item ->
            // New processor instance for each item if needed
            val itemProcessor = directDI.provider<ItemProcessor>()()
            itemProcessor.process(item, config)
            
            // Record metrics (lazy property)
            metricsCollector.record("item_processed", item.type)
        }
    }
}

// Error handling with direct access
class RobustService : DirectDIAware {
    override val directDI: DirectDI = serviceDirectDI
    
    fun robustOperation() {
        try {
            val primaryService = directDI.instance<PrimaryService>()
            primaryService.execute()
        } catch (e: DI.NotFoundException) {
            // Fallback to secondary service
            val fallbackService = directDI.instanceOrNull<FallbackService>()
            if (fallbackService != null) {
                fallbackService.execute()
            } else {
                throw ServiceUnavailableException("No service available", e)
            }
        } catch (e: DI.DependencyLoopException) {
            val emergencyService = new(::EmergencyService) // Bypass DI for emergency
            emergencyService.handleEmergency()
        }
    }
}

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