? 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或數(shù)據(jù)類。
-
Provider binding (提供者綁定)
該類型綁定在每一次需要獲取實例時,都會生成新的實例。該函數(shù)不接受任何參數(shù)并返回綁定類型的對象,例如:
() -> T。val di = DI { bind<Dice>() with provider { RandomDice(6) } } -
Singleton binding (單例綁定)
該類型綁定時,僅在第一次時通過單例函數(shù)懶加載創(chuàng)建實例,因此,所提供的函數(shù)僅被調(diào)用一次,該函數(shù)不接受任何參數(shù)并返回綁定類型的對象,例如:
() -> T。val di = DI { bind<DataSource>() with singleton { SqliteDS.open("path/to/file") } }-
非同步鎖單例
根據(jù)單例的定義,單例只能由一個實例,Kodein-DI使用同步互斥鎖來確保實例的唯一性。但是這么做會降低啟動性能。如果確保不需要使用同步鎖,可以禁止使用同步互斥鎖:
val di = DI { bind<DataSource>() with singleton(sync = false) { SqliteDS.open("path/to/file") } } -
餓漢模式
這與常規(guī)單例模式相同,唯一的區(qū)別是實例將在創(chuàng)建DI實例并定義所有綁定后立即生成。
val di = DI { bind<DataSource>() with eagerSingleton { SqliteDS.open("path/to/file") } }
-
-
Factory binding (工廠綁定)
該綁定將類型綁定到工廠函數(shù),該函數(shù)接收已定義類型的參數(shù)并返回綁定類型的對象,例如:
(A) -> T。一個工廠可以接受多個參數(shù)(最多五個)。
//被廢棄 val di = DI { bind<Dice>() with factory { startNumber: Int, sides: Int -> RandomDice(sides) } }由于多參數(shù)工廠很難使用,因此很快被棄用,官方推薦使用
data class對參數(shù)進行替換: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 (多重綁定)
一個多重綁定可以被認為是一個單例工廠,它保證在給定相同參數(shù)的情況下返回相同的對象。不同的參數(shù)返回不同的對象。
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)} } -
單例與多例綁定的引用
如果引用單例對象的實例沒有被銷毀,就能保證引用對象是同一個。
引用對象的單例或多禮除了普通的構(gòu)造函數(shù)之外,kodein-DI針對JVM還提供了三種引用:
-
Soft & weak
這些對象在給定的時間內(nèi)保證單例對象是同一個,但是在應(yīng)用程序周期內(nèi)不保證是同一個。如果沒有更多的強引用,GC可能會回收并重新創(chuàng)建它。因此,在應(yīng)用程序的生命周期中,可能多次調(diào)用提供的函數(shù),也可能不會多次調(diào)用。
val di = DI { //受軟引用約束,JVM會在發(fā)生OutOfMemoryException之前對其進行GC bind<Map> with singleton(ref = softRefrence) { WorldMap() } //受弱引用約束,沒有直接引用時,JVM將回收它 bind<Client> with singleton(ref = weakRefrence) { id -> clientFromDB(id) } } -
Thread local
與標準綁定單例相同,唯一的區(qū)別是在不同的線程,kodein-DI將獲取不同的實例。
//創(chuàng)建不同線程的緩存對象 val di = DI { bind<Cache>() with singleton(ref = threadLocal) { LRUCache(16 * 1024) } }注:
ref = threadLocal在JavaScript中不可用。
-
-
實例綁定
這會將類型綁定到已經(jīng)存在的實例:
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" }
-
-
直接綁定
有時,如果要綁定與創(chuàng)建類型相同的實例,為了書寫簡單,可以將
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")) }直接綁定應(yīng)該謹慎使用,因為具體的依賴關(guān)系是一種相反的模式,會阻止模塊化和測試。
-
子類型綁定
kodein-DI允許注冊“子類型綁定工廠”,該工廠將針對提供的類型的子類型進行調(diào)用:
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可以將其依賴項傳遞給構(gòu)造參數(shù),比如,一個類需要兩個依賴:
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() }
}