深入理解Koin框架之koin-core

深入理解Koin

[toc]

koin是使用kotlin编写的一款轻量级依赖注入(DI)框架,是Android开发领域依赖注入框架的后起之秀,与目前主流的依赖注入框架Dagger2相比,它的原理更容易理解和掌握,大有取代Dagger2的趋势。本文将从实践经验出发,深入浅出的介绍koin的使用技巧和自己对koin源码的理解,希望可以为读者解决一些日常开发中的遇到问题和带来一些对koin框架原理的较为深入认识。由于是基于个人的认知和理解水平,如有疏漏和误解,恳请各位读者指正

一、Koin简介

koin是github上的一个开源项目,项目地址如下:https://github.com/InsertKoinIO/koin。本部分的内容主要来自该项目的描述。与Dagger编译时自动生成注入代码不一样的是,koin是不生成模版代码,而是使用一套DSL来定义对象的注入,因为,整个注入逻辑都可以通过源代码直观的反映出来,而Dagger2的注解方式隐含的逻辑的太多,学习成本比较搞,各种注解的作用,生成的代码的可读性等都是使用Dagger2的一些痛点,同时Dagger2不是kotlin编写,无法使用kotin的一些特性,在kotlin越来越流行的背景下,koin框架也越来越得到开发人员的认可。

由于服务端的依赖注入主要是Spring框架,所以Koin的主要应用领域还是Android平台,但从koin框架的架构来看,它也可以用于服务端和JAVA环境开发。以koin-core和koin-core-ext为基础库,koin以扩展的方式提供了android(androidx)和 ktor的支持。

核心库

  • koin-core
  • koin-core-ext

android

  • koin-android
  • koin-android-ext
  • koin-android-scope
  • koin-android-viewmodel

androidx

  • koin-androidx-ext
  • koin-androidx-fragment
  • koin-androidx-scope
  • koin-androidx-viewmodel

ktor

  • koin-ktor

koin整个工程的代码量不是很大,掌握起来不会有太大的难度。后续内容会详细介绍上述模块,同时会从实践的角度解读这些代码。

二、使用入门

koin作为一个应用框架,最重要的是了解如何使用,只有在逐步使用的过程中,随着遇到的问题一步一步的才产生对掌握原理的需求。因此,将使用入门放到正文的最前面,希望用简单的代码感性的认识一下koin是一个什么框架以及如何使用koin

2.1 依赖声明

目前koin的版本是2.1.5,以gradle的依赖声明方式来声明引入koin。引入核心包就可以开发的JAVA应用,如果是android,需要声明android扩展依赖,ktor开发同理

ext.koin_version = '2.1.5' 
//引入核心包
implementation "org.koin:koin-core:$koin_version"
implementation "org.koin:koin-core-ext:$koin_version"

//如果是android开发,引入下面的android扩展包
//implementation "org.koin:koin-android:$koin_version"
//implementation "org.koin:koin-android-scope:$koin_version"
//implementation "org.koin:koin-android-viewmodel:$koin_version"
//implementation "org.koin:koin-android-ext:$koin_version"

//引入androidx扩展
//implementation "org.koin:koin-androidx-scope:$koin_version"
//implementation "org.koin:koin-androidx-viewmodel:$koin_version"
//implementation "org.koin:koin-androidx-fragment:$koin_version"
//implementation "org.koin:koin-androidx-ext:$koin_version"

//如果是开发ktor应用,引入ktor扩展
//implementation "org.koin:koin-ktor:$koin_version"

2.2 例程

下面通过对源码中的例子coffee-maker来讲解一下koin的注入流程.

coffee-maker这个应用的逻辑很简单,有一家咖啡店(CoffeeApp)有一部咖啡机(CoffeeMaker),它可以煮咖啡(brew),同时咖啡机由加热器(Heater-接口),泵(Pump-接口)组成,在本例中加热器是一个电子加热器(ElectricHeater-实现Heater),泵是一个热虹吸式再沸器(Thermosiphon-实现Pump),因此抽象出以下基础模型:

interface Heater {
    fun on()
    fun off()
    fun isHot(): Boolean
}
class ElectricHeater : Heater {

    private var heating: Boolean = false

    override fun on() {
        println("~ ~ ~ heating ~ ~ ~")
        heating = true
    }

    override fun off() {
        heating = false
    }

    override fun isHot(): Boolean = heating
}
interface Pump {
    fun pump()
}
class Thermosiphon(private val heater: Heater) : Pump {
    override fun pump() {
        if (heater.isHot()) {
            println("=> => pumping => =>")
        }
    }
}
class CoffeeMaker(private val pump: Pump, private val heater: Heater) {

    fun brew() {
        heater.on()
        pump.pump()
        println(" [_]P coffee! [_]P ")
        heater.off()
    }
}

上面的三类模型是我们煮一杯咖啡需要用的对象,DI做目的是将类的对象生成和对象使用分离,因为类的对象生成通常是比较复杂且变动比较频繁。因此DI框架来负责对象生成,开发人员只需要告诉DI框架,我需要那种类型的对象即可,可以极大的减少开发人员的工作量。因此对于咖啡店(CoffeeApp)的来说,他只需要一台咖啡机。而咖啡机怎么组装的,有哪些零部件,他并不关心。

class CoffeeApp : KoinComponent {
    val maker: CoffeeMaker by inject()
}

这里的maker(咖啡机)不直接调用CoffeeMaker构造方法(因为调用构造方法还得先构造出Heater 和 Pump对象,太复杂了),而是使用by inject() 来向框架要一个CoffeeMaker 对象。拿到对象之后就可以调用CoffeeMaker的brew方法来煮咖啡了。

我们来看以下煮一杯咖啡的完整流程:

fun main() {
    startKoin {
        printLogger()
        modules(listOf(coffeeAppModule))
    }
    val coffeeShop = CoffeeApp()
    coffeeShop.maker.brew()
    stopKoin()
}

startKoin 函数是启动koin框架的入口,传入一个定义在KoinApplication上的一个扩展lamda类型KoinAppDeclaration来配置koin。
modules 是KoinApplication的一个成员函数,来配置这个koinApplication有哪些module,本例中传入listOf(coffeeAppModule)是配置依赖的地方。

val coffeeAppModule = module {
    single { CoffeeMaker(get(), get()) }
    single { Thermosiphon(get()) }
    single { ElectricHeater() }
}

module 是一个顶层函数,接受一个定义在Module上的扩展lamda类型ModuleDeclaration来配置这个module

single 是Module的一个成员方法,它接受一个定义在Scope上的扩展lamda类型Definition来配置在Scope(这里是rootScope)中生成一个具体的对象。single方法定义的对象在该scope中会缓存起来,多个地方依赖同一个对象

get是定义在Scope上的成员方法,自动进行类型推断。这里将是从rootScope中查找对应的对象定义,CoffeeMaker(get(), get())中,第一个体get会去查找Pump类的对象,第二个会去查找Heater类对象。这个两个对象正好是接下来的两行注入的。

stopKoin() 也是一个顶层函数,用于停止koin,清理koin中的对象

这个例程示范了koin的基本用法,也是koin DSL的基本语法

startKoin {//启动koin
    modules() //加载modules
}
  module  {//声明一个module
    single {//单例对象,多次依赖只产生一个对象
        Constructor()
    }
    factory {//工厂对象,每次依赖产生一个新对象
        Constructor()
    }
}
}
stopKoin() // 结束

三、 Koin进阶

相信看完例程后我们对koin有了一个比较感性的认识,它通过一个DSL来声明各个类如何生成对象(单例模式和工厂模式),如果一个类的构造方法需要依赖另外一个类的对象,则调用具有类型推断功能的get()从koin中查找这个对象即可。因此,这个module的DSL看起来非常的简洁。调用具有0个或者多个get()参数的构造方法即可。如另外一个例程中

fun perfModule400() = module {
    single { A1() } // new A1()
    single { B1(get()) }// new B1(A1)
    single { C1(get(), get()) }// new C1(A1,B1)
    single { D1(get(), get(), get()) } // new D1(A1,B1,C1)
    single { A2() } //new A2()
    single { B2(get()) } // new B2(A2)
    single { C2(get(), get()) } // new C2(A2,B2)
}

这里我们用的是最简单的注入场景,实际中可能会存在更加复杂的注入场景。下面来说明以下

3.1 Qualifer

上面的例程中,一个类只声明了一种创建对象的方法,因此koin查找的时候自然就找到这个对象,但是如果一个类需要生成多种类型的对象时,koin就无法确定使用哪一个对象。此时就需要Qualifer来标记不同的对象,相当于为创建这种对象的方式起一个别名,这与Dagger2是相似的,知识Koin并不是使用注解来实现的,而是定义了一个Qualifier接口,并提供了两个实现:

  • StringQualifier 以字符串为value的qualifier
  • TypeQualifier 以类为type,以类的全限定名为value的qualifier

同时提供了多个顶层函数来生成Qualifier,支持字符串,枚举和Class作为value,如下调用都可以生成一个qualifier,常用的是StringQualifier

name("qualifer_name")
named(enum)
qualifier("qualifer_name")
qualifier(enum)
named()
qualifier()

为了更好的理解和使用qualifier,我们修改以下煮咖啡场景。假设现ElectricHeater 有两种规格,高功率和低功率的,现在咖啡店需要两台咖啡机,高低功率各一台。

class SizedElectricHeater(val size:Int) : ElectricHeater() {//extend ElectricHeater
    override fun on() {
        println("~ ~ ~ heating ~ ~ ~ with ${size} W")
        heating = true
    }
}

注入配置如下:

val coffeeAppModule = module {
    single(name("high")) { CoffeeMaker(get(name("high")), get(name("high"))) }
    single(name("low")) { CoffeeMaker(get(name("low")), get(name("low"))) }
    single(named("high")) { Thermosiphon(get(named("high"))) }
    single(named("low")) { Thermosiphon(get(named("low"))) }
    single(named("high")) { SizedElectricHeater(3000) }
    single(named("low")) { SizedElectricHeater(1000) }
}
class CoffeeApp : KoinComponent {
    val lowMaker: CoffeeMaker by inject(named("low"))
    val highMaker: CoffeeMaker by inject(named("high"))
}

namedqualifer 是可以混用的,他们返回的都是StringQualifier或者TypeQualifier

3.2 Scope

注入对象都是有作用范围的,如果没有指定scope的话就是koin的一个rootScope,如果指定scope,注入时就会从该scope中去查找声明的对象
如上面的例子,由于Heater有2种功率的产品,所以在在module里面Pump,CoffeeMaker都声明了high和low,我们可以使用scope来简化这种场景

 val coffeeAppModule = module {
      scope(named("low")) {
        scoped {// this will be single
            SizedElectricHeater(1000)
        }
        scoped {
            Thermosiphon(get())
        }
        scoped {
            CoffeeMaker(get(), get())
        }
    }
    scope(named("high")) {
        scoped {// this will be single
            SizedElectricHeater(3000)
        }.bind(Heater::class)
        scoped {
            Thermosiphon(get())
        }.bind(Pump::class)
        scoped {
            CoffeeMaker(get(), get())
        }
    }
}

这里声明了两个scope, 他们的qualifier 是 name("low"),named("high"),这样配置之后,在使用这些对方的时候,可以按照下面的方式去获取

 val lowMaker by getKoin().getOrCreateScope("lowScope", named("low")).inject();
 val highMaker by getKoin().getOrCreateScope("highScope", named("high")).inject() ;

这里有几个新的函数需要说明一下

scope 是Module的成员函数,用于在一个模块内自定义scope,它接受一个qualifier来标志这个scope和一个定义在ScopeDSL上的一个lamda扩展函数,
scoped 是ScopeDSL的一个成员函数,用于定义一个single类型的实例
bind 是定义在BeanDefinition上的扩展函数,用于配置该对象的兼容的类

 scoped {
       Thermosiphon(get())
 }.bind(Pump::class)

由于ThermosiphonPump的子类,而查询对象时,查找的是Pump的对象,因此Thermosiphon这个对象定义需要bindPump::class才能找到这个对象。

3.3 Parameter

在上面的例子中,因为Heater增加了一个功率参数,导致注入配置发生一系列的变化,那如果功率的参数是在获取对象的时候传入的话,就更加灵活。Koin也是是支持在获取对象的时候传入构造参数的。Koin的构造参数是通过DefinitionParameters来描述的,它支持最多穿5个参数

scope(named("parameter2")) {
        scoped {
                (size:Int) -> SizedElectricHeater(size)
        }.bind(Heater::class)
        scoped {
            (size:Int) -> Thermosiphon(get {
                parametersOf(size)
            })
        }.bind(Pump::class)
        scoped {
            (size:Int) -> CoffeeMaker(get { parametersOf(size)},get { parametersOf(size)})
        }
    }

val paramteredMaker2 by getKoin().getOrCreateScope("param2", named("parameter2")).inject {
        parametersOf(5500)
    };
 

在这个scope中,定义了一个带参数的SizedElectricHeaterThermosiphon构造的时候,通过通过
parametersOf传递构造参数。在最外层的app里面,才传递最终的参数5000.这里有两个语法。

  • (size:Int) -> SizedElectricHeater(size)这个表达式是定义在Scope上的lamda扩展函数,用于定义参数格式
  • parametersOf顶层函数通过一个vararg构造出一个DefinitionParameters类型的参数
3.4 Module

Module是一组关系较为密切的对象模块,它定义了一个模块内定义的对象的一些通用属性,主要有

  • createAtStart :对象是否是在startKoin的时候就创建
  • override: 当一个对象存在多个相同的定义时,后面的定义是否覆盖前面的定义
  • rootScope:当没有指定scope时,使用rootScope,当指定的scope中没有查到到定义时,会在rootScope中继续查找
  • otherScopes:自定义的scope列表,当调用module的成员方法scope的时候就可以创建一个自定义的scope

module提供两个成员方法,singlefactory来配置注入的对象

single 相当于是单例模式,多次注入为同一个对象

factory 相当于工厂模式,每次注入一个新的对象

3.5 KoinComponent

KoinComponent 是对象注入的场所,通常业务场景里需要实现这个接口,当然也可以不实现而直接使用koin,因为它只是封装了的koin的几个方法,使得应用程序可方便的获取到注入的对象。总的来说,它有三个重要方法

get 立即从koin中获取指定的对象

inject 使用延迟加载从koin中获取指定的对象

bind 获取一个同时绑定了主类和次类的对象,返回次类对象

 scope(named("high")) {
        scoped {
            SizedElectricHeater(3000)// 主类
        }.bind(Heater::class)//次类
 }
val highHeater = getKoin().getOrCreateScope("high", named("high"))
    .bind()

返回一个Heater类型的对象,它实际是一个SizedElectricHeater(3000)对象

总的来说,koin是非常精巧的一个注入框架,得益于kotlin语言的扩展函数,建立了一套注入的DSL,成功的摆脱了代码生成和反射这两种比较重的实现方式。在理解它的DSL基础上,整个框架的学习成本相对来说比较低。上面讲解的Scope,Qualifier,Module,KoinComponent和Parameter这几个重要概念,其中很多也是借鉴了Dagger2的架构,因此如果有Dagger2的开发经验的话,迁移到koin也是比较容易的。从应用的角度看,熟练使用上述的几个模型,就能满足大部分的业务开发需要。而下面开始,将要深入的源码里去深入理解koin,所有模型和它的DSL

四、原理分析

本章节主要基于源码来分析koin的原理,将围绕koin的运行状态,模块加载,Scope管理,DSL中各个模型的运行机制和对象注入流程这几个核心内容来讲解。

4.1 koin的运行状态

与koin的运行状态相关的类和方法如下:

  • Koin 核心类,封装了内部关于module加载,scope管理,对象注入,属性注入等内部模块,对外暴露应用接口。
  • KoinContext(实现类GlobalContext),koin的上下文,管理一个koin对象,负责koin对象的注册和关闭,应用中可以通过KoinContext来获取koin对象
  • KoinApplication。负责配置一个koin,比如需要为koin配置哪些模块,输入哪些属性和日志平台配置,是koin的入口。
  • KoinContextHandler koinContext的持有者单例对象,可以方便的获取一个注册的koinContext。
  • ContextFunctions.kt 定义了启动koin的便捷顶层方法startKoin

koin的启动过程就是生成一个KoinApplication,它有一个koin成员变量,然后这个KoinAppliation会给koin装载一系列的module和属性,然后将这个koin交给一个KoinContext,最后将这个koinContext 存到KoinContextHandler,以便后续使用。

下面结合源码来讲解一下

  • startKoin
fun startKoin(koinContext: KoinContext = GlobalContext(), appDeclaration: KoinAppDeclaration): KoinApplication {
    KoinContextHandler.register(koinContext) //(1)
    val koinApplication = KoinApplication.init()//(2)
    KoinContextHandler.start(koinApplication)//(3)
    appDeclaration(koinApplication) //(4)
    koinApplication.createEagerInstances() //(5)
    return koinApplication
}

首先生成一个koinContext,默认值为GlobalContext(), appDeclaration 是KoinApplication上的一个扩展lamda表达式,用于配置KoinApplication

typealias KoinAppDeclaration = KoinApplication.() -> Unit

(1) 将koinContext注册到KoinContextHandler,一个应用只能注册一次,只有一个koinContext,注册之后就可以在任何地方通过KoinContextHandler.getContext()取到这个koinContext

fun register(koinContext: KoinContext) = synchronized(this) {
        if (_context != null) {
            error("A KoinContext is already started")
        }
        _context = koinContext
    }
    

(2)KoinApplication.init()是KoinApplication的一个静态工厂方法,返回一个初始化好的KoinApplication

companion object {

        /**
         * Create a new instance of KoinApplication
         */
        @JvmStatic
        fun init(): KoinApplication {
            val app = KoinApplication()
            app.init()
            return app
        }
    }

Koin是KoinApplication的成员变量,在初始化KoinApplication的时候,生成Koin的对象,

init 时创建koin的rootScope定义

    class KoinApplication private constructor() {

        val koin = Koin() // 生成koin对象

        internal fun  init() {
            koin._scopeRegistry.createRootScopeDefinition()// 创建rootScope定义
        }

(3)KoinContextHandler.start(koinApplication) 将koinApplication与koinContext绑定,

    fun start(koinApplication: KoinApplication) {
        getContext().setup(koinApplication)
    }

GlobalContext:

    override fun setup(koinApplication: KoinApplication) = synchronized(this) {
        if (_koin != null) {
            throw KoinAppAlreadyStartedException("A Koin Application has already been started")
        }
        _koin = koinApplication.koin
    }

(4)在koinApplication,Koin,KoinContext 都准备好之后,再回调appDeclaration(koinApplication)回到应用自己的配置逻辑,并将这个koinApplication传出去。下面就是对这个koinApplication的简单配置说明(printLogger 和modules 都是koinApplication的成员方法)

    startKoin {
        printLogger()// 使用System.out.print来输出日记
        modules(listOf(coffeeAppModule))// 加载modules
    }

(5)koinApplication.createEagerInstances(),应用配置按照自己的需要配置好koinapplication,接着又回到框架的逻辑,创建eager的对象(最终会进入到InstanceRegistry的createEagerInstances)


    internal fun createEagerInstances() {
        instances.values.filterIsInstance>()
                .filter { instance -> instance.beanDefinition.options.isCreatedAtStart }
                .forEach { instance ->
                    instance.get(
                            InstanceContext(_koin, _scope)
                    )
                }
    }
    

即将些配置了isCreateAtStart的对象,在startKoin的时候就提前创建好对象,相当于是预加载。到这里,koin的环境就完全搭建起来了,在需要注入对象的地方,可以直接通过koin的来获取。

4.2 模块加载

在上面KoinApplication的配置中说到,会调用koinApplition的modules来配置注入的模块,现在来看看加载这些模块的逻辑。

KoinApplication.modules

  fun modules(modules: List): KoinApplication {
        if (koin._logger.isAt(Level.INFO)) {
            val duration = measureDuration {
                loadModules(modules) 
            }
            val count = koin._scopeRegistry.size()
            koin._logger.info("loaded $count definitions - $duration ms")
        } else {
            loadModules(modules)//(1)
        }
        if (koin._logger.isAt(Level.INFO)) {
            val duration = measureDuration {
                koin.createRootScope()
            }
            koin._logger.info("create context - $duration ms")
        } else {
            koin.createRootScope() //(2)
        }
        return this
    }
    

modules传入一个List,然后会调用loadModuleskoin.createRootScope()

(1) KoinApplication.loadModules

    private fun loadModules(modules: List) {
        koin.loadModules(modules)
    }

进入Koin.loadModules

    fun loadModules(modules: List) = synchronized(this) {
        _modules.addAll(modules)
        _scopeRegistry.loadModules(modules)
    }

首先,将modules都存入到koin的_modules字段中,然后接着调用_scopeRegistry.loadModules

ScopeRegistry.loadModules

    internal fun loadModules(modules: Iterable) {
        modules.forEach { module ->
            if (!module.isLoaded) {
                loadModule(module)
                module.isLoaded = true
            } else {
                _koin._logger.error("module '$module' already loaded!")
            }
        }
    }

循环调用ScopeRegistry.loadModule

    private fun loadModule(module: Module) {
        declareScope(module.rootScope)
        declareScopes(module.otherScopes)
    }

每加载一个module,会将个module相关的的rootScope和otherScopes为参数调用到declareScope。module的rootScope和otherScopes都是ScopeDenifition类型的对象,经过declare之后,会生成正在的Scope对象

ScopeRegistry. declareScope

    private fun declareScope(scopeDefinition: ScopeDefinition) {
        declareDefinitions(scopeDefinition)
        declareInstances(scopeDefinition)
    }

ScopeRegistry.declareDefinitions

    private fun declareDefinitions(definition: ScopeDefinition) {
         if (scopeDefinitions.contains(definition.qualifier.value)) {
              mergeDefinitions(definition)
            } else {
                _scopeDefinitions[definition.qualifier.value] = definition.copy()
            }
     }
     

scopeDefinitions是scopeRegistry的属性,类型是Map,如果已经存在一个qualifer相同的scopeDenifition,则进行合并,复杂将新的scopeDenifition存起来。mergeDefinitions的逻辑相对复杂。

ScopeRegistry.mergeDefinitions

    private fun mergeDefinitions(definition: ScopeDefinition) {
        val existing = scopeDefinitions[definition.qualifier.value]
            ?: error("Scope definition '$definition' not found in $_scopeDefinitions")
        definition.definitions.forEach {
            existing.save(it)
        }
    }

首先找到已经存在的那个scopeDenifition。scopeDenifition的主要结构是一个Set,即这个scope中定义了哪些对象。合并的时候就是将传入的scopeDefinition中定义的对象save到已经存在的scopeDenifition中去.

BeanDenifition.save

    fun save(beanDefinition: BeanDefinition<*>, forceOverride: Boolean = false) {
        if (definitions.contains(beanDefinition)) {
            if (beanDefinition.options.override || forceOverride) {
                _definitions.remove(beanDefinition)
            } else {
                val current = definitions.firstOrNull { it == beanDefinition }
                throw DefinitionOverrideException("Definition '$beanDefinition' try to override existing definition. Please use override option or check for definition '$current'")
            }
        }
        _definitions.add(beanDefinition)
    }

如果之前已经存在一个相同的beanDefinition的话,需要新的beanDenition是不是强制override或者module是强制override.如果是的话,移除旧的,然后加入新的BeanDefinition,否则会报异常DefinitionOverrideException。因此override是一个比较重要的配置。我们回到前面,看看beanDefinition.options.overrideforceOverride 这两个的值是如何确定的。

(1) 首先是Module会进行全局的定义

    class Module(
        val createAtStart: Boolean,
        val override: Boolean
     )

例如:

    val overridedModule = module(override = true) {
         single { 
             ElectricHeater()
         }
    }

(2) 在BeanDenifion中,成员options中包含有override属性

    data class BeanDefinition(
        val scopeDefinition: ScopeDefinition,
        val primaryType: KClass<*>,
        val qualifier: Qualifier? = null,
        val definition: Definition,
        val kind: Kind,
        val secondaryTypes: List> = listOf(),
        val options: Options = Options(),
        val properties: Properties = Properties(),
        val callbacks: Callbacks = Callbacks()
) 
    data class Options(var isCreatedAtStart: Boolean = false, var override: Boolean = false)
    

而这个options是在mudule的single或者factory方法生成BeanDenifition的时候生成的

     inline fun  single(
            qualifier: Qualifier? = null,
            createdAtStart: Boolean = false,
            override: Boolean = false,
            noinline definition: Definition
    ): BeanDefinition {
        return Definitions.saveSingle(
                qualifier,
                definition,
                rootScope,
                makeOptions(override, createdAtStart)
        )
    }
    fun makeOptions(override: Boolean, createdAtStart: Boolean = false): Options =
            Options(this.createAtStart || createdAtStart, this.override || override)

因此可以看到,override的值的确定逻辑是由module和single/factory方法共同决定,任何一个为true。则BeanDefinion的override为true

上面介绍的是ScopeRegistry.declareDefinitions,是ScopeRegistry.declareScope中的一个主要分支,这个分支,将Module中定义的的BeanDefinition放到了正确的ScopeDefinition中。第二个分支是ScopeRegistry.declareInstances(scopeDefinition),这个方法将BeanDefinition转换成具体的生成对象的工厂,在注入对象的时候,将会使用这些工厂来生成具体的实例。

ScopeRegistry.declareInstances

     private fun declareInstances(scopeDefinition: ScopeDefinition) {
        _scopes.values.filter { it._scopeDefinition == scopeDefinition }.forEach { it.loadDefinitions(scopeDefinition) }
    }

_scopes是已经存在的Scope对象,初识时,scopes为空的,因此,在开始loadModules时,并不会出现scope中的BeanDefinition转换成工厂,但是如果已经存在这样的scope时,就会进入到Scope.loadDefinitions(scopeDefinition)

Scope.loadDefinitions:

    fun loadDefinitions(scopeDefinition: ScopeDefinition) {
            scopeDefinition.definitions.forEach {
             _instanceRegistry.createDefinition(it)
            }
     }

_instanceRegistry是Scope的一个InstanceRegistry类型的成员变量,这里遍历scopeDefinition中定义的BeanDefinitions(definitions),调用_instanceRegistry.createDefinition来转换成InstantFactory

InstanceRegistry. createDefinition

    internal fun createDefinition(definition: BeanDefinition<*>) {
        saveDefinition(definition, false)
    }

InstanceRegistry. saveDefinition

    fun saveDefinition(definition: BeanDefinition<*>, override: Boolean) {
        val defOverride = definition.options.override || override
        val instanceFactory = createInstanceFactory(_koin, definition) //(1)
        saveInstance(//(2)
                indexKey(definition.primaryType, definition.qualifier),
                instanceFactory,
                defOverride
        )
        definition.secondaryTypes.forEach { clazz ->//(3)
            if (defOverride) {
                saveInstance(
                        indexKey(clazz, definition.qualifier),
                        instanceFactory,
                        defOverride
                )
            } else {
                saveInstanceIfPossible(
                        indexKey(clazz, definition.qualifier),
                        instanceFactory
                )
            }
        }
    }

InstanceRegistry. createInstanceFactory

    private fun createInstanceFactory(
            _koin: Koin,
            definition: BeanDefinition<*>
    ): InstanceFactory<*> {
        return when (definition.kind) {
            Kind.Single -> SingleInstanceFactory(_koin, definition)
            Kind.Factory -> FactoryInstanceFactory(_koin, definition)
        }

(1)首先,根据single还是factory方法定义的生成不同的InstantaceFactory。

(2)将工厂关联到主类

(3) 将工厂关联到所有的次类

至此,module的加载逻辑全部结束。需要注意的是,BeanDefinition转InstanceFactory的逻辑并不是每次都会执行的。如果scope不存在的话,这部分逻辑是不执行的。还有几个疑问是,BeanDefinition的属性比较多,比如primaryType和secondaryTypes 这两个字段对BeanDefinition转InstanceFactory都有影响,那他们是什么意思,如何配置的,这个也是比较重要的内容。但不管怎么说,我们loadModule的主线逻辑我们已经分析完毕。后面会再次补充细节一些的分支逻辑。

(2)在KoinApplication.loadModules结束后,接着做的是koin.createRootScope(),创建rootScope对象

Koin.createRootScope:

  fun createRootScope() {
      _scopeRegistry.createRootScope()
  }

ScopeRegistry.createRootScope:

    internal fun createRootScope() {
        if (_rootScope == null) {
            _rootScope =
                createScope(ScopeDefinition.ROOT_SCOPE_ID, ScopeDefinition.ROOT_SCOPE_QUALIFIER, null)
        }
    }  
    

ScopeRegistry. createScope:

    fun createScope(scopeId: ScopeID, qualifier: Qualifier, source: Any? = null): Scope {
        if (scopes.contains(scopeId)) {
            throw ScopeAlreadyCreatedException("Scope with id '$scopeId' is already created")
        }

        val scopeDefinition = scopeDefinitions[qualifier.value]
        return if (scopeDefinition != null) {
            val createdScope: Scope = createScope(scopeId, scopeDefinition, source)
            _scopes[scopeId] = createdScope//(1)
            createdScope
        } else {
            throw NoScopeDefFoundException("No Scope Definition found for qualifer '${qualifier.value}'")
        }
    }
    

_scopes 是一个Map,新创建的Scope对象会存入到ScopeRegistry的_scopes里面

ScopeRegistry. createScope:

  private fun createScope(scopeId: ScopeID, scopeDefinition: ScopeDefinition, source: Any?): Scope {
        val scope = Scope(scopeId, scopeDefinition, _koin, source)
        val links = _rootScope?.let { listOf(it) } ?: emptyList()
        scope.create(links)
        return scope
    }

调用Scope的构造方法,然后将_rootScope作为links,调用scope的create方法

4.3 模块卸载

加载的module也可以卸载,卸载时,会将这个module从加载的module集合中移除,同时会将这个module中声明的所有的scope中的BeanDenifition,InstanceFactory都移除,并将module.isLoaded设置成false

fun unloadModules(modules: List) = synchronized(this) {
        _scopeRegistry.unloadModules(modules)
        _modules.removeAll(modules)
    }
    fun unloadModules(modules: List) = synchronized(this) {
        _scopeRegistry.unloadModules(modules)
        _modules.removeAll(modules)
    }
    fun unloadModules(modules: Iterable) {
        modules.forEach {
            unloadModules(it)
        }
    }
    
    fun unloadModules(module: Module) {
        val scopeDefinitions = module.otherScopes + module.rootScope
        scopeDefinitions.forEach {
            unloadDefinitions(it)
        }
        module.isLoaded = false
    }
    
    private fun unloadDefinitions(scopeDefinition: ScopeDefinition) {
        unloadInstances(scopeDefinition)
        _scopeDefinitions.values.firstOrNull { it == scopeDefinition }?.unloadDefinitions(scopeDefinition)
    }

    private fun unloadInstances(scopeDefinition: ScopeDefinition) {
        _scopes.values.filter { it._scopeDefinition == scopeDefinition }.forEach { it.dropInstances(scopeDefinition) }
    }
  

这里需要注意的是,卸载module时,module声明的scope对象和scopeDefinition对象仍然保留在ScopeRegistry中,只是这些scope中与这个module相关的BeanDefinition和InstanceFactory被清空了。

4.4 scope管理

关于Scope有两个概念,一个是ScopeDefinition,是关于scope的定义,这个是在module里面通过scope函数来定义的;另一是Scope,是根据ScopeDefinition生成的真正的Scope对象,koin 使用ScopeRegistry来管理Scope 和 ScopeDefinition,其中ScopeDefinition时在loadModule的时候注册的,在Module初始化时就会生成一个rootScope的ScopeDefinition,scope函数生成的会scopeDefinition放到module.otherScope.

Module初始化:

    class Module(
        val createAtStart: Boolean,
        val override: Boolean
    ) {
         val rootScope: ScopeDefinition = ScopeDefinition.rootDefinition()
         ......

Module.scope:

    fun scope(qualifier: Qualifier, scopeSet: ScopeDSL.() -> Unit) {
        val scopeDefinition = ScopeDefinition(qualifier)
        ScopeDSL(scopeDefinition).apply(scopeSet)
        otherScopes.add(scopeDefinition)
    }

而真正生成Scope的地方是在需要注入对象的地方。总的说来有两个地方。一个是上文说到的,在koinApplication.loadModules结束后,会创建好rootScope,另外一个是手动调用koin.createScope等方法。在讲createRootScope的时候,已经讲述了createScope函数,这里就不在重复。这里主要讲一下Scope本身属性

  • scopeId,String的别名,Scope的ID
  • _source,Any,如果在scope中没有找到对象,此时返回_soure
  • _linkedScope,关联的其他scope,主要是rootScope
  • _instanceRegistry,解析好的对象工厂管理器

Scope包含了koin的主要业务逻辑,koin的很多接口也是委托到scope来实现。ScopeRegsitry是注册Scope的组件。因此要创建或者获取一个scope都是通过ScopeRegsitry来实现的,我们来看一下scope的获取逻辑

Koin.getOrCreateScope:

    fun getOrCreateScope(scopeId: ScopeID, qualifier: Qualifier): Scope {
        return _scopeRegistry.getScopeOrNull(scopeId) ?: createScope(scopeId, qualifier)
    }

可以看到,标志一个scope需要两个维度,一个是scopeId,另外一个qualifier。首先以scopeId来查询是否有存在的scope
ScopeRegister.getScopeOrNull:

    fun getScopeOrNull(scopeId: ScopeID): Scope? {
        return scopes[scopeId]
    }

在ScopeRegistry中用一个Map来存放已经创建的scope,key为ScopeID。如果没有找到,就创建

ScopeRegister.createScope:

     fun createScope(scopeId: ScopeID, qualifier: Qualifier, source: Any? = null): Scope {
        if (scopes.contains(scopeId)) {
            throw ScopeAlreadyCreatedException("Scope with id '$scopeId' is already created")
        }

        val scopeDefinition = scopeDefinitions[qualifier.value]
        return if (scopeDefinition != null) {
            val createdScope: Scope = createScope(scopeId, scopeDefinition, source)
            _scopes[scopeId] = createdScope
            createdScope
        } else {
            throw NoScopeDefFoundException("No Scope Definition found for qualifer '${qualifier.value}'")
        }
    }

这段逻辑比较简单,如果已经存在相同的scopeId了,则抛出异常ScopeAlreadyCreatedException。否则以qualifer去查找scopeDefinition,如果存在这样的scopeDefinition,则用该scopeDefinition创建一个新的scope,然后记录下载并当作结果返回,否则报异常NoScopeDefFoundException,scopeId 对与scope本身来说是没有意义的。从这段代码我们可以看出,如果scopeId与第一个scope绑定后,如果这个scope删除后,是可以绑定到另外一个qualifiered的scope,即一个scopeId是有可能对应不同qualifer的scope的。使用时也需要注意,尽量不要出现这种情况。

scope也是可以被删除的

Koin.deleteScope:

    fun deleteScope(scopeId: ScopeID) {
         _scopeRegistry.deleteScope(scopeId)
    }   

ScopeRegister.deleteScope:

    fun deleteScope(scopeId: ScopeID) {
        _scopes.remove(scopeId)
    }
4.6 Module 和Scope的关系
  1. scope由module来定义。也就是ScopeDefinition由module来生成
  2. scope对象一旦生成,就与module没有太大关系,因为scope 和scopeDefintion对象生成后,就交给ScopeRegistry管理,scopeRegistry是koin的成员变量,因此可以不局限于定义的module内,是koin全局共享的
4.7 对象查找过程

对象的查找逻辑最终是在scope中实现

Scope.get:

    fun  get(
        clazz: KClass<*>,
        qualifier: Qualifier? = null,
        parameters: ParametersDefinition? = null
    ): T {
        return if (_koin._logger.isAt(Level.DEBUG)) {
            val qualifierString = qualifier?.let { " with qualifier '$qualifier'" } ?: ""
            _koin._logger.debug("+- '${clazz.getFullName()}'$qualifierString")
            val (instance: T, duration: Double) = measureDurationForResult {
                resolveInstance(qualifier, clazz, parameters)
            }
            _koin._logger.debug("|- '${clazz.getFullName()}' in $duration ms")
            return instance
        } else {
            resolveInstance(qualifier, clazz, parameters)
        }
    }

入参三个:

  1. 请求对象的class,
  2. 对象的限定符qulifier,可选,默认为null
  3. 生成对象的参数,可选,默认为null

然后会进入到resolveInstance函数

    private fun  resolveInstance(
        qualifier: Qualifier?,
        clazz: KClass<*>,
        parameters: ParametersDefinition?
    ): T {
        if (_closed) {
            throw ClosedScopeException("Scope '$id' is closed")
        }
        //TODO Resolve in Root or link
        val indexKey = indexKey(clazz, qualifier)
        return _instanceRegistry.resolveInstance(indexKey, parameters)
            ?: findInOtherScope(clazz, qualifier, parameters) ?: getFromSource(clazz)
            ?: throwDefinitionNotFound(qualifier, clazz)
    }   

查找是由三个层级:

  1. 首先在当前的scope中查找,如果没有查找,则
  2. 在linkedScope中查找,默认是rootScope,如若还没有找到,则
  3. 尝试使用source对象,如果source的类型不一致则
  4. 抛出异常。

因此需要要注意的是,一个module里声明的对象,不能依赖到另外modudle中自定义scope中声明的对象,但是可以依赖到其他module中的rootScope中声明的对象。

    val module1 = module {

    single {
        CoffeeMaker(get { parametersOf(7500) }, get { parametersOf(7500) })
    }
}   
val module3 = module(override = true) {
    single { (size: Int) ->
        SizedElectricHeater(size);
    }.bind(Heater::class)

    single { (size: Int) ->
        Thermosiphon(get {
            parametersOf(size)
        })
    }.bind(Pump::class)
}

可以成功的获取到module1中定义的CoffeeMaker

    val paramteredModuel2Maker = getKoin().get()

但是如果是这样:

    val module1 = module {
     single {
            CoffeeMaker(get { parametersOf(7500) }, get { parametersOf(7500) })
        }
    }
        
    val module2 = module() {
       scope(named("module2")) {
         scoped { (size: Int) ->
                    SizedElectricHeater(size);
         }.bind(Heater::class)
        }
        scope(named("module2")) {
            scoped { (size: Int) ->
                    Thermosiphon(get {
                    parametersOf(size)
            })
            }.bind(Pump::class)
        }
    }

则不能找到CoffeeMaker的定义。

作如下修改:

    val module1 = module {
     single(named('module2')){
            CoffeeMaker(get { parametersOf(7500) }, get { parametersOf(7500) })
        }
    }

就又可以通过如下的方式获取到CoffeeMaker对象

    val paramteredModuel2Maker = getKoin().get(named("module2"))

因为在module1 和module2中都声明了named("module2")这个scope,在这个scope里声明了依赖的三个对象

你可能感兴趣的:(深入理解Koin框架之koin-core)