美文网首页
Kodein-DI 7.0.0(三):声明依赖

Kodein-DI 7.0.0(三):声明依赖

作者: 何意悲欢0_o | 来源:发表于2020-08-06 10:21 被阅读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() }
}

相关文章

网友评论

      本文标题:Kodein-DI 7.0.0(三):声明依赖

      本文链接:https://www.haomeiwen.com/subject/dluorktx.html