禅与计算机程序设计艺术Android开发经验谈Kotlin编程

Kodien原理详解

2020-04-21  本文已影响0人  LSteven

一个依赖注入框架,无非就是注入与获取实例。那么kodein是怎么注入的?

注入

val kodein = Kodein {
    bind<Die>() with singleton { Die(instance()) } 

    bind<Random>() with singleton { Random(instance) } 
    constant(tag "max") with 5 
}

可以看到首先要初始化Kodien环境,

KodeinImpl

internal open class KodeinImpl internal constructor(private val _container: KodeinContainerImpl) : Kodein {

    @Suppress("unused")
    private constructor(builder: KodeinMainBuilderImpl, runCallbacks: Boolean) : this(KodeinContainerImpl(builder.containerBuilder, builder.externalSources, builder.fullDescriptionOnError, runCallbacks))

    constructor(allowSilentOverride: Boolean = false, init: Kodein.MainBuilder.() -> Unit) : this(newBuilder(allowSilentOverride, init), true)

    companion object {
        private fun newBuilder(allowSilentOverride: Boolean = false, init: Kodein.MainBuilder.() -> Unit) = KodeinMainBuilderImpl(allowSilentOverride).apply(init)

        fun withDelayedCallbacks(allowSilentOverride: Boolean = false, init: Kodein.MainBuilder.() -> Unit): Pair<Kodein, () -> Unit> {
            val kodein = KodeinImpl(newBuilder(allowSilentOverride, init), false)
            return kodein to { kodein._container.initCallbacks?.invoke() ; Unit }
        }
    }

    final override val container: KodeinContainer by lazy {
        if (_container.initCallbacks != null)
            throw IllegalStateException("Kodein has not been initialized")
        _container
    }

}

这里会内部构建好一个Kodien.Builder,调用初始化函数后,传入一个container

这里KodienBuilder通过调用bind返回一个binder,供上层去绑定一个实例

Binders

所以我们看到builder其实提供了一个bind方法,对外暴露接口获取binder。

inner class TypeBinder<T : Any> internal constructor(val type: TypeToken<out T>, val tag: Any?, val overrides: Boolean?) : Kodein.Builder.TypeBinder<T> {
    internal val containerBuilder get() = this@KodeinBuilderImpl.containerBuilder

    override infix fun <C, A> with(binding: KodeinBinding<in C, in A, out T>) = containerBuilder.bind(Kodein.Key(binding.contextType, binding.argType, type, tag), binding, moduleName, overrides)
}

inner class DirectBinder internal constructor(private val _tag: Any?, private val _overrides: Boolean?) : Kodein.Builder.DirectBinder {
    override infix fun <C, A, T: Any> from(binding: KodeinBinding<in C, in A, out T>) {
        if (binding.createdType == UnitToken) {
            throw IllegalArgumentException("Using `bind() from` with a *Unit* ${binding.factoryName()} is most likely an error. If you are sure you want to bind the Unit type, please use `bind<Unit>() with ${binding.factoryName()}`.")
        }
        containerBuilder.bind(Kodein.Key(binding.contextType, binding.argType, binding.createdType, _tag), binding, moduleName, _overrides)
    }
}

inner class ConstantBinder internal constructor(private val _tag: Any, private val _overrides: Boolean?) : Kodein.Builder.ConstantBinder {
    @Suppress("FunctionName")
    override fun <T: Any> With(valueType: TypeToken<out T>, value: T) = Bind(tag = _tag, overrides = _overrides) from InstanceBinding(valueType, value)
}

然后我们来看看binder可以绑定哪些binding

Bindings

/**
 * @param C The type of the context used by the retriever.
 * @param A The type of argument used to create or retrieve an instance.
 * @param T The type of instance this factory creates or retrieves.
*/
interface Binding<C, A, T: Any> {

    fun getFactory(kodein: BindingKodein<C>, key: Kodein.Key<C, A, T>): (A) -> T
}

Binding是什么,就是根据key返回一个构造器,这个构造器接受一个参数返回一个实例

Factory

class Factory<C, A, T: Any>(override val contextType: TypeToken<in C>, override val argType: TypeToken<in A>, override val createdType: TypeToken<out T>, private val creator: BindingKodein<C>.(A) -> T) : KodeinBinding<C, A, T> {

    override fun factoryName() = "factory"

    override fun getFactory(kodein: BindingKodein<C>, key: Kodein.Key<C, A, T>): (A) -> T = { arg -> this.creator(kodein, arg) }
}

Multiton

class Multiton<C, A, T: Any>(override val scope: Scope<C>, override val contextType: TypeToken<in C>, override val argType: TypeToken<in A>, override val createdType: TypeToken<out T>, refMaker: RefMaker? = null, val sync: Boolean = true, private val creator: SimpleBindingKodein<C>.(A) -> T) : KodeinBinding<C, A, T> {
    private val _refMaker = refMaker ?: SingletonReference

    private val _scopeId = Any()


    override fun getFactory(kodein: BindingKodein<C>, key: Kodein.Key<C, A, T>): (A) -> T {
        val registry = scope.getRegistry(kodein.context)
        return { arg ->
            @Suppress("UNCHECKED_CAST")
            registry.getOrCreate(ScopeKey(_scopeId, arg), sync) { _refMaker.make { BindingContextedKodein(kodein, kodein.context).creator(arg) } } as T
        }
    }

    override val copier = KodeinBinding.Copier { Multiton(scope, contextType, argType, createdType, _refMaker, sync, creator) }
}

首先通过scope.getRegistry(kodein.context)拿到registry,啥意思,拿到一个存储实例的registry,这里可以和生命周期(context)绑定。

registry.getOrCreate(ScopeKey(_scopeId, arg), sync) { _refMaker.make { BindingContextedKodein(kodein, kodein.context).creator(arg) } } as T

对于scopeKey(是data class),可以看到如果arg一样是不会create的,如果不存在的话,通过creator()函数创建一个

Provider

class Provider<C, T: Any>(override val contextType: TypeToken<in C>, override val createdType: TypeToken<out T>, val creator: NoArgBindingKodein<C>.() -> T) : NoArgKodeinBinding<C, T> {
    override fun factoryName() = "provider"

    override fun getFactory(kodein: BindingKodein<C>, key: Kodein.Key<C, Unit, T>): (Unit) -> T = { NoArgBindingKodeinWrap(kodein).creator() }
}

Singleton

class Singleton<C, T: Any>(override val scope: Scope<C>, override val contextType: TypeToken<in C>, override val createdType: TypeToken<out T>, refMaker: RefMaker? = null, val sync: Boolean = true, val creator: NoArgSimpleBindingKodein<C>.() -> T) : NoArgKodeinBinding<C, T> {
    @Suppress("UNCHECKED_CAST")
    private val _refMaker = refMaker ?: SingletonReference
    private val _scopeKey = ScopeKey(Any(), Unit)

    /**
     * @see [KodeinBinding.getFactory]
     */
    override fun getFactory(kodein: BindingKodein<C>, key: Kodein.Key<C, Unit, T>): (Unit) -> T {
        val registry = scope.getRegistry(kodein.context)
        return {
            @Suppress("UNCHECKED_CAST")
            registry.getOrCreate(_scopeKey, sync) { _refMaker.make { NoArgBindingKodeinWrap(BindingContextedKodein(kodein, kodein.context)).creator() } } as T
        }
    }

    override val copier = KodeinBinding.Copier { Singleton(scope, contextType, createdType, _refMaker, sync, creator) }
}

InstanceBinding

class InstanceBinding<T: Any>(override val createdType: TypeToken<out T>, val instance: T) : NoArgKodeinBinding<Any?, T> {
    override fun factoryName() = "instance"
    override val contextType = AnyToken

    /**
     * @see [KodeinBinding.getFactory]
     */
    override fun getFactory(kodein: BindingKodein<Any?>, key: Kodein.Key<Any?, Unit, T>): (Unit) -> T = { this.instance }


}

就返回一个已经初始化好的instance

Eager

class EagerSingleton<T: Any>(builder: KodeinContainer.Builder, override val createdType: TypeToken<out T>, val creator: NoArgSimpleBindingKodein<Any?>.() -> T) : NoArgKodeinBinding<Any?, T> {

    override val contextType = AnyToken

    @Volatile private var _instance: T? = null
    private val _lock = Any()

    private fun getFactory(kodein: BindingKodein<Any?>): (Unit) -> T {
        return { _ ->
            synchronizedIfNull(
                    lock = _lock,
                    predicate = this@EagerSingleton::_instance,
                    ifNotNull = { it },
                    ifNull = {
                        NoArgBindingKodeinWrap(kodein).creator().also { _instance = it }
                    }
            )
        }
    }


    init {
        val key = Kodein.Key(AnyToken, UnitToken, createdType, null)
        builder.onReady { getFactory(BindingKodeinImpl(this, key, null, 0)).invoke(Unit) }
    }

    override val copier = KodeinBinding.Copier { builder -> EagerSingleton(builder, createdType, creator) }
}

builder.onReady,当kodein容器??准备好时就通过getFactory初始化实例

这里我们暂时知道bindings就是返回一个函数,这个函数传入参数能返回我们需要的实例T。

那么binderbinding是怎么结合起来的,就通过一个初始化好的KodeinContainerBuilder去对bindings进行配置。

KodeinContainerBuilderImpl

配置bindings

internal class KodeinContainerBuilderImpl(
        allowOverride: Boolean,
        silentOverride: Boolean,
        internal val bindingsMap: MutableMap<Kodein.Key<*, *, *>, MutableList<KodeinDefining<*, *, *>>>,
        internal val callbacks: MutableList<DKodein.() -> Unit>,
        internal val translators: MutableList<ContextTranslator<*, *>>
)

配置能否override,持有bindingMap,

Bind

当开始绑定时,会将绑定关系放入bindingMap

val bindings = bindingsMap.getOrPut(key) { newLinkedList() }
bindings.add(0, KodeinDefining(binding, fromModule))

Extend

配置可以从已有的kodeinContainer中继承

  override fun extend(container: KodeinContainer, allowOverride: Boolean, copy: Set<Kodein.Key<*, *, *>>) {
        checkMatch(allowOverride)

        container.tree.bindings.forEach { (key, bindings) ->
            if (!allowOverride)
                checkOverrides(key, null)

            val newBindings = if (key in copy) {
                newLinkedList<KodeinDefining<*, *, *>>().also {
                    bindings.mapTo(it) { KodeinDefining(it.binding.copier?.copy(this@KodeinContainerBuilderImpl) ?: it.binding, it.fromModule) }
                }
            }
            else {
                newLinkedList<KodeinDefining<*, *, *>>(bindings)
            }

            bindingsMap[key] = newBindings
        }

        translators += container.tree.registeredTranslators
    }

获取实例

前面bb了这么多,那怎么获取实例呢?

private val viewModel: AlbumListViewModel by instance()

fun <T : Any> KodeinAware.Instance(type: TypeToken<out T>, tag: Any? = null): KodeinProperty<T> =
        KodeinProperty(kodeinTrigger, kodeinContext) { ctx, _ -> kodein.container.provider(Kodein.Key(ctx.anyType, UnitToken, type, tag), ctx.value).invoke() }

这里返回一个lazy属性,

KodeinProperty

kodein使用lazy能力。

通过定义 provideDelegate 操作符,可以扩展创建属性实现所委托对象的逻辑:
解释

interface LazyDelegate<out V> {
    /** @suppress */
    operator fun provideDelegate(receiver: Any?, prop: KProperty<Any?>): Lazy<V>
}


class KodeinProperty<out V>(internal val trigger: KodeinTrigger?, val originalContext: KodeinContext<*>, private val get: (KodeinContext<*>, String) -> V) : LazyDelegate<V> {

    override fun provideDelegate(receiver: Any?, prop: KProperty<Any?>): Lazy<V> = lazy {
        @Suppress("UNCHECKED_CAST")
        val context = if (receiver != null && originalContext === AnyKodeinContext) KodeinContext(TTOf(receiver) as TypeToken<in Any>, receiver) else originalContext
        get(context, prop.name) } .also { trigger?.properties?.add(it)
    }

}

class KodeinPropertyMap<in I, out O>(private val base: KodeinProperty<I>, private val map: (I) -> O) : LazyDelegate<O> {

    override fun provideDelegate(receiver: Any?, prop: KProperty<Any?>): Lazy<O> = lazy { map(base.provideDelegate(receiver, prop).value) }.also { base.trigger?.properties?.add(it) }

}

翻译一下:
假设一个var prop: Type by KodeinProperty() =>

class C {
    // calling "provideDelegate" to create the additional "delegate" property
    private val prop$delegate = KodeinProperty().provideDelegate(this, this::prop)
    val prop: Type
        get() = prop$delegate.getValue(this, this::prop)
}

这里返回了lazy,即当这个属性被用到时,会调用get方法

那我们再来看看这段代码:

fun <A, T : Any> KodeinAware.Factory(argType: TypeToken<in A>, type: TypeToken<out T>, tag: Any? = null): KodeinProperty<(A) -> T> =
        KodeinProperty(kodeinTrigger, kodeinContext) { ctx, _ -> kodein.container.factory(Kodein.Key(ctx.anyType, argType, type, tag), ctx.value) }

看源码可以看到Factory&Instance的不同,前者lazy返回一个N阶函数,后者lazy直接返回一个实例。

所以很简单,就是通过kodein.container.factory去获取。

KodeinContainerImpl

获取factory,什么是工厂,就是别人传入一个A(arg)返回自己需要的实例。

tree.find(key)

首先在树中根据键值寻找,kodein在绑定关系的时候初始化了一个containerimpl, 初始化了一颗树,

tree.find(key, 0).let {
    if (it.size == 1) {
        val (_, definition, translator) = it[0]
        node?.check(key, 0)
        val kContext = translator?.toKContext(context) ?: KodeinContext(key.contextType, context) as KodeinContext<Any>
        key as Kodein.Key<Any, A, T>
        val bindingKodein = bindingKodein(key, kContext, definition.tree, overrideLevel)
        return definition.binding.getFactory(bindingKodein, key)
    }
}

如果找到一个(有且一个),拿到definition,即binding的定义。检查是否有循环依赖

bindingKodein
val bindingKodein = bindingKodein(key, kContext, definition.tree, overrideLevel)
return definition.binding.getFactory(bindingKodein, key)

这里会创建一个bindingKodein的东西,在bindingsgetFactory,为什么要传入这么一个东西呢?

NoArgBindingKodeinWrap(BindingContextedKodein(kodein, kodein.context)).creator()

我们知道在实例化一个类时,这个类可能还会依赖其他类,绑定可能如下:

bind<AlbumDetailViewModel>() with scoped<Fragment>(AndroidLifecycleScope).singleton {
    KotlinViewModelProvider.of(context) { AlbumDetailViewModel(instance(), instance()) }
}

在里面还需要instance()实例化,那为什么不能直接用kodein的环境呢,因为按官网说法,还可以进行override:

val testModule = Kodein.Module(name = "test") {
    bind<Logger>(overrides = true) with singleton { FileLoggerWrapper("path/to/file", overriddenInstance()) } 
}

所以其实bindingKodein也是kodein环境:

internal open class BindingKodeinImpl<out C, out A, out T: Any> internal constructor(
        override val dkodein: DKodein,
        private val _key: Kodein.Key<C, A, T>,
        override val context: C,
        private val _overrideLevel: Int
) : DKodein by dkodein, BindingKodein<C> {
    override fun overriddenFactory(): (Any?) -> Any = container.factory(_key, context, _overrideLevel + 1) as (Any?) -> Any
    override fun overriddenFactoryOrNull(): ((Any?) -> Any)? = container.factoryOrNull(_key, context, _overrideLevel + 1) as ((Any?) -> Any)?
}

它包括什么,它包括DKodien, Dkodien又是一个新的概念,

If you don’t want to use delegated properties, Kodein has you covered. Most of the features available to Kodein are available to DKodein (D is for Direct). DKodein allows you to directly get a new instance or dependency

它就比kodein少了delegate属性,因此也没有receiver & lazy的概念。

override fun overriddenFactory(): (Any?) -> Any = container.factory(_key, context, _overrideLevel + 1) as (Any?) -> Any

正常绑定时使用的是container.factory(0),这里会使用+1表明使用override

循环依赖 node.check

val kodein = Kodein {
    bind<Die>() with singleton { Die(instance()) } 

    bind<Random>() with singleton { Random(instance) } 
    constant(tag "max") with 5 
}
org.kodein.di.Kodein$DependencyLoopException: Dependency recursion:
     bind<View>()
    ╔╩>bind<Presenter>()
    ║  ╚>bind<View>()
    ╚════╝

回顾一下,
,当我们调用by instance时会发生什么,会返回一个函数,函数执行时会调用
kodein.container.provider(Kodein.Key(ctx.anyType, UnitToken, type, tag), ctx.value).invoke(),即调用creator,即Die(instance()),这里调用instance()时,背后调用DKodeinBaseImpl.container.provider.invoke(),那这里的container是谁呢,先看DKodeinBaseImpl是谁,就是调用by instancecontainer,所以这里container都是同一个,很正常能看出循环依赖。

我们看,在找factory时

private tailrec fun recursiveCheck(node: Node, searchedKey: Kodein.Key<*, *, *>, searchedOverrideLevel: Int): Boolean {
    return if (node.key == searchedKey && node.overrideLevel == searchedOverrideLevel)
        false
    else if (node.parent == null)
        true
    else
        recursiveCheck(node.parent, searchedKey, searchedOverrideLevel)
}

开始NodeDie, 然后NodeRandom,然后 searchKeydie, 因此在递归上去时node -> parent又是die,导致循环依赖。

KodeinTreeImpl

一个缓存:

private val _cache: MutableMap<Kodein.Key<*, *, *>, Triple<Kodein.Key<*, *, *>, List<KodeinDefinition<*, *, *>>, ContextTranslator<*, *>?>>
private typealias BoundTypeTree = MutableMap<TypeChecker, ContextTypeTree>

private typealias ContextTypeTree = MutableMap<TypeChecker.Down, ArgumentTypeTree>

private typealias ArgumentTypeTree = MutableMap<TypeChecker.Down, TagTree>

private typealias TagTree = MutableMap<Any?, Kodein.Key<*, *, *>>

其中因为kodien可以override,所以会有definition lists:

val kodein = Kodein {
    bind<API>() with singleton { APIImpl() }
    /* ... */
    bind<API>(overrides = true) with singleton { OtherAPIImpl() }
}

寻找过程

Inject or Retrieve

inject

class Controller(private val ds: DataSource) {
    /*...*/
}
val controller by kodein.newInstance { Controller(instance()) }

这里会走到Controller(instance()), 在instance里会自己去取实例。

retrive

class Controller(override val kodein: Kodein): KodeinAware {
    private val ds: DataSource by instance()
}

这两种方式,一种是自己就是一个kodein = KodeinAware 内部会去取实例,另一种是自己由外部注入。

SubTypes

val kodein = Kodein {
    bind<Controller>().subtypes() with { type ->
        when (type.jvmType) { 
            is MySpecialController::class.java -> singleton { MySpecialController() }
            else -> provider { myControllerSystem.getController(type.jvmType) }
        }
    }
}

看到这里问题,我绑定的是T,这个T的子类也会找到:

data class Down(override val type: TypeToken<*>) : TypeChecker() {
    val isAny = (type == AnyToken)
    override fun check(other: TypeToken<*>) = isAny || type.isAssignableFrom(other)
}

Contexted & Scoped

kodien中,所有binder with binding都会带一个scope&context:对于默认的是AnyToken&NoScope

override val contextType = AnyToken

override val scope: Scope<Any?> get() = NoScope() // Recreating a new NoScope every-time *on purpose*!

那么,假设我们想要绑到一个具体的context&scope时,可以按如下这么写:

val kodein = Kodein {
    bind<Writer>() with contexted<Request>.provider { context.response.writer } 
}
val kodein = Kodein {
    bind<Session>() with scoped(requestScope).singleton { context.openSession() } 
}

val session: Session by kodein.on(context = request).instance()

Scopes are derived from the context variable.

这里context<Request> & scoped(requestScope)会返回一个新的builder,绑定新的context&scope, 当使用kodein.on时会创建一个新的kodein,kodeinContext是传入的request,我们再来重看一下getFactory:

override fun getFactory(kodein: BindingKodein<C>, key: Kodein.Key<C, A, T>): (A) -> T {
    val registry = scope.getRegistry(kodein.context)
    return { arg ->
        @Suppress("UNCHECKED_CAST")
        registry.getOrCreate(ScopeKey(_scopeId, arg), sync) { _refMaker.make { BindingContextedKodein(kodein, kodein.context).creator(arg) } } as T
    }
}

我们举个scope的例子:

open class AndroidLifecycleScope private constructor(private val newRegistry: () -> ScopeRegistry) : Scope<LifecycleOwner> {

    companion object multiItem: AndroidLifecycleScope(::StandardScopeRegistry)

    object singleItem: AndroidLifecycleScope(::SingleItemScopeRegistry)

    private val map = HashMap<LifecycleOwner, ScopeRegistry>()

    override fun getRegistry(context: LifecycleOwner): ScopeRegistry {
        return synchronizedIfNull(
                lock = map,
                predicate = { map[context] },
                ifNotNull = { it },
                ifNull = {
                    val registry = newRegistry()
                    map[context] = registry
                    context.lifecycle.addObserver(object : LifecycleObserver {
                        @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
                        fun onDestroy() {
                            context.lifecycle.removeObserver(this)
                            registry.clear()
                            map.remove(context)
                        }
                    })
                    registry
                }
        )
    }
}

TypedReference

p05ut8ksb6.png

讲的挺好

private fun Type._checkIsReified(disp: Any) {
    when (val jvmType = javaType) {
        is Class<*> -> {}
        is ParameterizedType -> for (arg in jvmType.actualTypeArguments) arg._checkIsReified(disp)
        is GenericArrayType -> jvmType.genericComponentType._checkIsReified(disp)
        is WildcardType -> {
            for (arg in jvmType.lowerBounds)
                arg._checkIsReified(disp)
            for (arg in jvmType.upperBounds)
                arg._checkIsReified(disp)
        }
        is TypeVariable<*> -> throw IllegalArgumentException("$disp uses a type variable named ${jvmType.name}, therefore, the bound value can never be retrieved.")
        else -> throw IllegalArgumentException("Unknown type ${jvmType.javaClass} $jvmType")
    }
}

反正就是一定要具体化泛型,不要有<T>这种

上一篇下一篇

猜你喜欢

热点阅读