雑に見るKoinの内部実装
この間諸用があってKoinの内部実装を見ないと親友のセリヌンティウスが邪智暴虐の王ディオニスに処刑されちゃう運びだったのでKoinをのぞいて見ました。
みなさんのアプリはみなさんの子供です。そこに出どころのわからない謎の物体(オブジェクト)が注入されていると言う事実、耐えられますか?私は耐えられない。どこの男のものかもわからない物体を我が娘に注入するなんて言語道断。どこから注入されているのか探っていきましょう。
雑な概要
本記事の内容はKoinのバージョン2.0.1に関するものです。
タイトルが雑に見るKoinの内部実装なのでまず雑に概要をば。
Koinはアノテーションによるコード生成を行わずにDIを実現しているのですが、中身ではシングルトンなインスタンスに様々を保存しておいて、by inject()
などでそれを引っ張ってくるシンプルな方法でこれを行っています。
ザッツオール!!!!
説明
『Koin物語 再会のミネラルタウン』での主要登場人物は下の7人です。「イカれたメンバーを紹介するぜ!」って感じですね。
登場人物 | 役割 |
---|---|
GlobalContext |
シングルトンオブジェクト。KoinApplication を持ってる。 |
KoinApplication |
Koin を持つ人。 |
Koin |
普通に触ってるとこいつが一番目立つ。Scope を持つ。 |
Module |
BeanDefinition を持つ。複数存在することがあり得る。アリエル。 |
BeanDefinition |
大事。Koinでインジェクトするオブジェクトの情報と、そのオブジェクトを生成する関数オブジェクトを持つ。 |
Scope |
BeanRegistry を持つ。 |
BeanRegistry |
全てのModule が持つBeanDefinition をまとめて持つ。 |
いや多いな
さて、イカれたメンバーの紹介も終わったことなので実際にKoinを使用する手順に従って中身を見ていきましょう。
startKoinまで
val myModule = module { single { Controller(get()) } } startKoin { modules(listOf(myModule)) }
まずはApplicationに書くであろうここです。
まず、module { }
についてです。
これModule
型のインスタンスを返すトップレベル関数として定義されており、普段中にsingle { Controller(get()) }
とか書く部分は、Module.() -> Unit
型となっています。
module{ }
は内部でModule
型のインスタンスを生成し、それに対して各Module.() -> Unit
を実行してからこれを返します。
また、single<T> { }
はその内部で、返り値となるModule
にT
に対応したBeanDefinition
を保存します。
なんかごちゃって来ましたが、つまり、val myModule = module { single { Controller(get()) } }
時点でController
のインスタンスを生成する関数、Controller(get())
がこのmyModule
に保存されます。
次にstartKoin { modules(listOf(myModule)) }
ですね。
startKoin { }
はGlobalContext
からKoinApplication
を取り出してそれを返します。
その際に、Koin
内のScope
にmodules()
に入れたBeanDefinition
を保存します。
また、single(createdAtStart = true) { Foo() }
で宣言したインスタンスはこの時に生成されます。
ここまでをまとめると、各インスタンスを生成する() -> T
をmyModule
を経由してKoin
内に保存します。(詳しく言えばKoin
内のScope
のBeanRegistry
に保存されます。)
使用する際
ここまででDIする準備は整ったので、あとはこれを取り出すだけですね!
val controller: Controller by inject
では、遅延プロパティ(by lazy
)でgetKoin.get<T>()
が呼ばれています。
getKoin()
では、シングルトンなインスタンスであるGlobalContext
からKoinApplication
内のKoin
を返しています。先のstartKoin { }
で様々を保存したヤツを引っ張ってくる訳です。
その後のget<T>()
ではScope
内のBeanRegistry
に保存してある、そのT
を返す関数を実行してこれを返します。
single
の場合、そのインスタンスが既に作成されているならそれと同じインスタンスを返します。
フィニッシュ!
まとめ
どうやらみなさんの娘さんに得体の知れない物体を注入してるのはこういった仕組みによるものだったようです。
よく考えてみれば最初に自分で出したもの(Applicationで定義した() -> T
)を実行して返してるだけなので、汗が蒸発して巡り巡って飲用水になって帰ってくるみたいな雰囲気でしたね!!!!!