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开发同理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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),因此抽象出以下基础模型:
1 2 3 4 5 | interface Heater { fun on() fun off() fun isHot(): Boolean } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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 } |
1 2 3 | interface Pump { fun pump() } |
1 2 3 4 5 6 7 | class Thermosiphon(private val heater: Heater) : Pump { override fun pump() { if (heater.isHot()) { println("=> => pumping => =>") } } } |
1 2 3 4 5 6 7 8 9 | 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)的来说,他只需要一台咖啡机。而咖啡机怎么组装的,有哪些零部件,他并不关心。
1 2 3 | class CoffeeApp : KoinComponent { val maker: CoffeeMaker by inject() } |
这里的maker(咖啡机)不直接调用CoffeeMaker构造方法(因为调用构造方法还得先构造出Heater 和 Pump对象,太复杂了),而是使用by inject() 来向框架要一个CoffeeMaker 对象。拿到对象之后就可以调用CoffeeMaker的brew方法来煮咖啡了。
我们来看以下煮一杯咖啡的完整流程:
1 2 3 4 5 6 7 8 9 | 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)是配置依赖的地方。
1 2 3 4 5 | val coffeeAppModule = module { single { CoffeeMaker(get(), get()) } single<Pump> { Thermosiphon(get()) } single<Heater> { 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的基本语法
1 2 3 | startKoin {//启动koin modules() //加载modules } |
1 2 3 4 5 6 7 8 9 | module {//声明一个module single {//单例对象,多次依赖只产生一个对象 Constructor() } factory {//工厂对象,每次依赖产生一个新对象 Constructor() } } } |
1 | stopKoin() // 结束 |
三、 Koin进阶
相信看完例程后我们对koin有了一个比较感性的认识,它通过一个DSL来声明各个类如何生成对象(单例模式和工厂模式),如果一个类的构造方法需要依赖另外一个类的对象,则调用具有类型推断功能的get()从koin中查找这个对象即可。因此,这个module的DSL看起来非常的简洁。调用具有0个或者多个get()参数的构造方法即可。如另外一个例程中
1 2 3 4 5 6 7 8 9 | 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
1 2 3 4 5 6 | name("qualifer_name") named<T>(enum<T>) qualifier("qualifer_name") qualifier<T>(enum<T>) named<T>() qualifier<T>() |
为了更好的理解和使用qualifier,我们修改以下煮咖啡场景。假设现ElectricHeater 有两种规格,高功率和低功率的,现在咖啡店需要两台咖啡机,高低功率各一台。
1 2 3 4 5 6 | class SizedElectricHeater(val size:Int) : ElectricHeater() {//extend ElectricHeater override fun on() { println("~ ~ ~ heating ~ ~ ~ with ${size} W") heating = true } } |
注入配置如下:
1 2 3 4 5 6 7 8 | val coffeeAppModule = module { single(name("high")) { CoffeeMaker(get(name("high")), get(name("high"))) } single(name("low")) { CoffeeMaker(get(name("low")), get(name("low"))) } single<Pump>(named("high")) { Thermosiphon(get(named("high"))) } single<Pump>(named("low")) { Thermosiphon(get(named("low"))) } single<Heater>(named("high")) { SizedElectricHeater(3000) } single<Heater>(named("low")) { SizedElectricHeater(1000) } } |
1 2 3 4 | class CoffeeApp : KoinComponent { val lowMaker: CoffeeMaker by inject(named("low")) val highMaker: CoffeeMaker by inject(named("high")) } |
named 和 qualifer 是可以混用的,他们返回的都是StringQualifier或者TypeQualifier
3.2 Scope
注入对象都是有作用范围的,如果没有指定scope的话就是koin的一个rootScope,如果指定scope,注入时就会从该scope中去查找声明的对象
如上面的例子,由于Heater有2种功率的产品,所以在在module里面Pump,CoffeeMaker都声明了high和low,我们可以使用scope来简化这种场景
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | val coffeeAppModule = module { scope(named("low")) { scoped {// this will be single SizedElectricHeater(1000) } scoped { Thermosiphon(get()) } scoped { CoffeeMaker(get<Thermosiphon>(), get<SizedElectricHeater>()) } } 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"),这样配置之后,在使用这些对方的时候,可以按照下面的方式去获取
1 2 | val lowMaker by getKoin().getOrCreateScope("lowScope", named("low")).inject<CoffeeMaker>(); val highMaker by getKoin().getOrCreateScope("highScope", named("high")).inject<CoffeeMaker>() ; |
这里有几个新的函数需要说明一下
scope 是Module的成员函数,用于在一个模块内自定义scope,它接受一个qualifier来标志这个scope和一个定义在ScopeDSL上的一个lamda扩展函数,
scoped 是ScopeDSL的一个成员函数,用于定义一个single类型的实例
bind 是定义在BeanDefinition上的扩展函数,用于配置该对象的兼容的类
1 2 3 | scoped { Thermosiphon(get()) }.bind(Pump::class) |
由于Thermosiphon是Pump的子类,而查询对象时,查找的是Pump的对象,因此Thermosiphon这个对象定义需要bind到Pump::class才能找到这个对象。
3.3 Parameter
在上面的例子中,因为Heater增加了一个功率参数,导致注入配置发生一系列的变化,那如果功率的参数是在获取对象的时候传入的话,就更加灵活。Koin也是是支持在获取对象的时候传入构造参数的。Koin的构造参数是通过DefinitionParameters来描述的,它支持最多穿5个参数
1 2 3 4 5 6 7 8 9 10 11 12 13 | scope(named("parameter2")) { scoped { (size:Int) -> SizedElectricHeater(size) }.bind(Heater::class) scoped { (size:Int) -> Thermosiphon(get<SizedElectricHeater> { parametersOf(size) }) }.bind(Pump::class) scoped { (size:Int) -> CoffeeMaker(get { parametersOf(size)},get { parametersOf(size)}) } } |
1 2 3 | val paramteredMaker2 by getKoin().getOrCreateScope("param2", named("parameter2")).inject<CoffeeMaker> { parametersOf(5500) }; |
在这个scope中,定义了一个带参数的SizedElectricHeater,Thermosiphon构造的时候,通过通过
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提供两个成员方法,single 和 factory来配置注入的对象
single 相当于是单例模式,多次注入为同一个对象
factory 相当于工厂模式,每次注入一个新的对象
3.5 KoinComponent
KoinComponent 是对象注入的场所,通常业务场景里需要实现这个接口,当然也可以不实现而直接使用koin,因为它只是封装了的koin的几个方法,使得应用程序可方便的获取到注入的对象。总的来说,它有三个重要方法
get 立即从koin中获取指定的对象
inject 使用延迟加载从koin中获取指定的对象
bind 获取一个同时绑定了主类和次类的对象,返回主类对象
1 2 3 4 5 | scope(named("high")) { scoped { SizedElectricHeater(3000)// 主类 }.bind(Heater::class)//次类 } |
1 2 | val highHeater = getKoin().getOrCreateScope("high", named("high")) .bind<Heater,SizedElectricHeater>() |
总的来说,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
1 2 3 4 5 6 7 8 | 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
1 | typealias KoinAppDeclaration = KoinApplication.() -> Unit |
(1) 将koinContext注册到KoinContextHandler,一个应用只能注册一次,只有一个koinContext,注册之后就可以在任何地方通过KoinContextHandler.getContext()取到这个koinContext
1 2 3 4 5 6 | fun register(koinContext: KoinContext) = synchronized(this) { if (_context != null) { error("A KoinContext is already started") } _context = koinContext } |
(2)KoinApplication.init()是KoinApplication的一个静态工厂方法,返回一个初始化好的KoinApplication
1 2 3 4 5 6 7 8 9 10 11 12 | 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定义
1 2 3 4 5 6 7 | class KoinApplication private constructor() { val koin = Koin() // 生成koin对象 internal fun init() { koin._scopeRegistry.createRootScopeDefinition()// 创建rootScope定义 } |
(3)KoinContextHandler.start(koinApplication) 将koinApplication与koinContext绑定,
1 2 3 | fun start(koinApplication: KoinApplication) { getContext().setup(koinApplication) } |
GlobalContext:
1 2 3 4 5 6 | 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的成员方法)
1 2 3 4 | startKoin { printLogger()// 使用System.out.print来输出日记 modules(listOf(coffeeAppModule))// 加载modules } |
(5)koinApplication.createEagerInstances(),应用配置按照自己的需要配置好koinapplication,接着又回到框架的逻辑,创建eager的对象(最终会进入到InstanceRegistry的createEagerInstances)
1 2 3 4 5 6 7 8 9 | internal fun createEagerInstances() { instances.values.filterIsInstance<SingleInstanceFactory<*>>() .filter { instance -> instance.beanDefinition.options.isCreatedAtStart } .forEach { instance -> instance.get( InstanceContext(_koin, _scope) ) } } |
即将些配置了isCreateAtStart的对象,在startKoin的时候就提前创建好对象,相当于是预加载。到这里,koin的环境就完全搭建起来了,在需要注入对象的地方,可以直接通过koin的来获取。
4.2 模块加载
在上面KoinApplication的配置中说到,会调用koinApplition的modules来配置注入的模块,现在来看看加载这些模块的逻辑。
KoinApplication.modules
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | fun modules(modules: List<Module>): 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
(1) KoinApplication.loadModules
1 2 3 | private fun loadModules(modules: List<Module>) { koin.loadModules(modules) } |
进入Koin.loadModules
1 2 3 4 | fun loadModules(modules: List<Module>) = synchronized(this) { _modules.addAll(modules) _scopeRegistry.loadModules(modules) } |
首先,将modules都存入到koin的_modules字段中,然后接着调用_scopeRegistry.loadModules
ScopeRegistry.loadModules
1 2 3 4 5 6 7 8 9 10 | internal fun loadModules(modules: Iterable<Module>) { modules.forEach { module -> if (!module.isLoaded) { loadModule(module) module.isLoaded = true } else { _koin._logger.error("module '$module' already loaded!") } } } |
循环调用ScopeRegistry.loadModule
1 2 3 4 | private fun loadModule(module: Module) { declareScope(module.rootScope) declareScopes(module.otherScopes) } |
每加载一个module,会将个module相关的的rootScope和otherScopes为参数调用到declareScope。module的rootScope和otherScopes都是ScopeDenifition类型的对象,经过declare之后,会生成正在的Scope对象
ScopeRegistry. declareScope
1 2 3 4 | private fun declareScope(scopeDefinition: ScopeDefinition) { declareDefinitions(scopeDefinition) declareInstances(scopeDefinition) } |
ScopeRegistry.declareDefinitions
1 2 3 4 5 6 7 | private fun declareDefinitions(definition: ScopeDefinition) { if (scopeDefinitions.contains(definition.qualifier.value)) { mergeDefinitions(definition) } else { _scopeDefinitions[definition.qualifier.value] = definition.copy() } } |
scopeDefinitions是scopeRegistry的属性,类型是Map
ScopeRegistry.mergeDefinitions
1 2 3 4 5 6 7 | 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
BeanDenifition.save
1 2 3 4 5 6 7 8 9 10 11 | 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.override 和 forceOverride 这两个的值是如何确定的。
(1) 首先是Module会进行全局的定义
1 2 3 4 | class Module( val createAtStart: Boolean, val override: Boolean ) |
例如:
1 2 3 4 5 | val overridedModule = module(override = true) { single { ElectricHeater() } } |
(2) 在BeanDenifion中,成员options中包含有override属性
1 2 3 4 5 6 7 8 9 10 11 | data class BeanDefinition<T>( val scopeDefinition: ScopeDefinition, val primaryType: KClass<*>, val qualifier: Qualifier? = null, val definition: Definition<T>, val kind: Kind, val secondaryTypes: List<KClass<*>> = listOf(), val options: Options = Options(), val properties: Properties = Properties(), val callbacks: Callbacks<T> = Callbacks() ) |
1 | data class Options(var isCreatedAtStart: Boolean = false, var override: Boolean = false) |
而这个options是在mudule的single或者factory方法生成BeanDenifition的时候生成的
1 2 3 4 5 6 7 8 9 10 11 12 13 | inline fun <reified T> single( qualifier: Qualifier? = null, createdAtStart: Boolean = false, override: Boolean = false, noinline definition: Definition<T> ): BeanDefinition<T> { return Definitions.saveSingle( qualifier, definition, rootScope, makeOptions(override, createdAtStart) ) } |
1 2 | 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
1 2 3 | 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:
1 2 3 4 5 | fun loadDefinitions(scopeDefinition: ScopeDefinition) { scopeDefinition.definitions.forEach { _instanceRegistry.createDefinition(it) } } |
_instanceRegistry是Scope的一个InstanceRegistry类型的成员变量,这里遍历scopeDefinition中定义的BeanDefinitions(definitions),调用_instanceRegistry.createDefinition来转换成InstantFactory
InstanceRegistry. createDefinition
1 2 3 | internal fun createDefinition(definition: BeanDefinition<*>) { saveDefinition(definition, false) } |
InstanceRegistry. saveDefinition
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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
1 2 3 4 5 6 7 8 | 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:
1 2 3 | fun createRootScope() { _scopeRegistry.createRootScope() } |
ScopeRegistry.createRootScope:
1 2 3 4 5 6 | internal fun createRootScope() { if (_rootScope == null) { _rootScope = createScope(ScopeDefinition.ROOT_SCOPE_ID, ScopeDefinition.ROOT_SCOPE_QUALIFIER, null) } } |
ScopeRegistry. createScope:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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:
1 2 3 4 5 6 | 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 scope管理
关于Scope有两个概念,一个是ScopeDefinition,是关于scope的定义,这个是在module里面通过scope函数来定义的;另一是Scope,是根据ScopeDefinition生成的真正的Scope对象。在Module初始化时就会生成一个rootScope的ScopeDefinition,scope函数生成的会scopeDefinition放到module.otherScope.
Module初始化:
1 2 3 4 5 6 | class Module( val createAtStart: Boolean, val override: Boolean ) { val rootScope: ScopeDefinition = ScopeDefinition.rootDefinition() ...... |
Module.scope:
1 2 3 4 5 | 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,解析好的对象工厂管理器
(未完待续。。。)