Kodein-DI 7.0.0(三):聲明依賴

? 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,不僅僅為Stringtag類型必須支持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() }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容