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比较,因此官方建议使用基本类型,StringInt或数据类。

  • 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() }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,548评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,497评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,990评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,618评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,618评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,246评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,819评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,725评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,268评论 1 320
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,356评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,488评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,181评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,862评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,331评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,445评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,897评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,500评论 2 359