Kodein-DI依赖在DI容器中声明:
val di = DI {
/* Bindings */
}
一个声明总是以bind<Type>() with
开头,Kodein-DI提供了多种绑定方式:
-
Tagged bindings (标记绑定)
可以使用标记进行绑定,为了解决在相同类型下具有不同实例的情况:
val di = DI {
bind<Dice>() with...
bind<Dice>(tag = "DnD10") with...
bind<Dice>(tag = "DnD20") with...
}
tag
的类型为Any
,不仅仅为String
,tag
类型必须支持equality & hashcode
比较,因此官方建议使用基本类型,String
、Int
或数据类。
-
Provider binding (提供者绑定)
该类型绑定在每一次需要获取实例时,都会生成新的实例。该函数不接受任何参数并返回绑定类型的对象,例如:
() -> T
。val di = DI { bind<Dice>() with provider { RandomDice(6) } }
-
Singleton binding (单例绑定)
该类型绑定时,仅在第一次时通过单例函数懒加载创建实例,因此,所提供的函数仅被调用一次,该函数不接受任何参数并返回绑定类型的对象,例如:
() -> T
。val di = DI { bind<DataSource>() with singleton { SqliteDS.open("path/to/file") } }
-
非同步锁单例
根据单例的定义,单例只能由一个实例,Kodein-DI使用同步互斥锁来确保实例的唯一性。但是这么做会降低启动性能。如果确保不需要使用同步锁,可以禁止使用同步互斥锁:
val di = DI { bind<DataSource>() with singleton(sync = false) { SqliteDS.open("path/to/file") } }
-
饿汉模式
这与常规单例模式相同,唯一的区别是实例将在创建DI实例并定义所有绑定后立即生成。
val di = DI { bind<DataSource>() with eagerSingleton { SqliteDS.open("path/to/file") } }
-
-
Factory binding (工厂绑定)
该绑定将类型绑定到工厂函数,该函数接收已定义类型的参数并返回绑定类型的对象,例如:
(A) -> T
。一个工厂可以接受多个参数(最多五个)。
//被废弃 val di = DI { bind<Dice>() with factory { startNumber: Int, sides: Int -> RandomDice(sides) } }
由于多参数工厂很难使用,因此很快被弃用,官方推荐使用
data class
对参数进行替换:data class DiceParams(val startNumber: Int, val sides: Int) val di = DI { bind<Dice>() with factory { params: DiceParams -> RandomDice(params) } } //to use class Controller(override val di: DI) : DIAware { private val factory by factory<DiceParams, Dice>() private val dice: Dice = factory.invoke(DiceParams(1, 2)) /* ... */ } //or recommend : class Controller(override val di: DI) : DIAware { private val dice: Dice by instance(arg = DiceParams(1, 2)) /* ... */ }
-
Multiton binding (多重绑定)
一个多重绑定可以被认为是一个单例工厂,它保证在给定相同参数的情况下返回相同的对象。不同的参数返回不同的对象。
val di = DI { bind<Dice>() with multiton { max: Int -> RandomDice(max)} } //to use class Controller1(override val di: DI, arg: Int) : DIAware { private val dice: Dice by instance(arg = arg) /* ... */ }
与单例绑定一样,可以禁止同步锁:
val di = DI { bind<Dice>() with multiton(sync = false) { max: Int -> RandomDice(max)} }
-
单例与多例绑定的引用
如果引用单例对象的实例没有被销毁,就能保证引用对象是同一个。
引用对象的单例或多礼除了普通的构造函数之外,kodein-DI针对JVM还提供了三种引用:
-
Soft & weak
这些对象在给定的时间内保证单例对象是同一个,但是在应用程序周期内不保证是同一个。如果没有更多的强引用,GC可能会回收并重新创建它。因此,在应用程序的生命周期中,可能多次调用提供的函数,也可能不会多次调用。
val di = DI { //受软引用约束,JVM会在发生OutOfMemoryException之前对其进行GC bind<Map> with singleton(ref = softRefrence) { WorldMap() } //受弱引用约束,没有直接引用时,JVM将回收它 bind<Client> with singleton(ref = weakRefrence) { id -> clientFromDB(id) } }
-
Thread local
与标准绑定单例相同,唯一的区别是在不同的线程,kodein-DI将获取不同的实例。
//创建不同线程的缓存对象 val di = DI { bind<Cache>() with singleton(ref = threadLocal) { LRUCache(16 * 1024) } }
注:
ref = threadLocal
在JavaScript中不可用。
-
-
实例绑定
这会将类型绑定到已经存在的实例:
val di = DI { bind<DataSource>() with instance(SqliteDataSource.open("path/to/file")) }
-
常量绑定
有时候,需要绑定配置常量:
val di = DI { constant(tag = "macThread") with 8 constant(tag = "serverUrl") with "https://my.server.url" }
-
-
直接绑定
有时,如果要绑定与创建类型相同的实例,为了书写简单,可以将
bind<Type>() with...
变更为bind() from...
val di = DI { bind() from singleton { RandomDice(6) } bind("DnD20") from provider { RandomRice(20) } bind() from instance(SqliteDataSource.open("path/to/file")) }
直接绑定应该谨慎使用,因为具体的依赖关系是一种相反的模式,会阻止模块化和测试。
-
子类型绑定
kodein-DI允许注册“子类型绑定工厂”,该工厂将针对提供的类型的子类型进行调用:
val di = DI { bind<Dice>().subTypes() with { typeToken -> when (typeToken.jvmType) { RandomDice::class.java -> provider { RandomDice() } else -> singleton { DetailDice() } } } } //to use class Controller(override val di: DI) : DIAware { private val dice: Dice by instance() /* init DetailDice */ } class Controller(override val di: DI) : DIAware { private val dice: RandomDice by instance() /* init RandomDice */ }
-
依赖传递
对于延迟实例化的实例,一个实例通常需要多个依赖项。kodein-DI可以将其依赖项传递给构造参数,比如,一个类需要两个依赖:
class Die(private val random:Random,private val sides:Int){ /*...*/ }
通过
instance
就可以将依赖项与他的传递依赖项绑定在一起:val di = DI { //通过instance(),instance(tag)获得依赖传递项 bind<Die> with singleton { Die(instance(),instance(tag = "max")) } //依赖传递 bind<Random>() with provider { SecureRandom() } constant("max") with 5 }
-
传递工厂依赖
val di = DI {
bind<DataSource>() with singleton { MySQLDataSource() }
//使用DataSource作为可传递工厂依赖项
bind<Connection>() with privider { instance<DataSource>().openConnection() }
}
网友评论