雑に見る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> { }はその内部で、返り値となるModuleTに対応したBeanDefinitionを保存します。

なんかごちゃって来ましたが、つまり、val myModule = module { single { Controller(get()) } }時点でControllerインスタンスを生成する関数、Controller(get())がこのmyModuleに保存されます。

次にstartKoin { modules(listOf(myModule)) }ですね。
startKoin { }GlobalContextからKoinApplicationを取り出してそれを返します。

その際に、Koin内のScopemodules()に入れたBeanDefinitionを保存します。
また、single(createdAtStart = true) { Foo() }で宣言したインスタンスはこの時に生成されます。

ここまでをまとめると、各インスタンスを生成する() -> TmyModuleを経由してKoin内に保存します。(詳しく言えばKoin内のScopeBeanRegistryに保存されます。)

使用する際

ここまででDIする準備は整ったので、あとはこれを取り出すだけですね!

val controller: Controller by inject

では、遅延プロパティ(by lazy)でgetKoin.get<T>()が呼ばれています。
getKoin()では、シングルトンなインスタンスであるGlobalContextからKoinApplication内のKoinを返しています。先のstartKoin { }で様々を保存したヤツを引っ張ってくる訳です。
その後のget<T>()ではScope内のBeanRegistryに保存してある、そのTを返す関数を実行してこれを返します。
singleの場合、そのインスタンスが既に作成されているならそれと同じインスタンスを返します。

フィニッシュ!

まとめ

どうやらみなさんの娘さんに得体の知れない物体を注入してるのはこういった仕組みによるものだったようです。
よく考えてみれば最初に自分で出したもの(Applicationで定義した() -> T)を実行して返してるだけなので、汗が蒸発して巡り巡って飲用水になって帰ってくるみたいな雰囲気でしたね!!!!!

明日はnegito6さんです。お楽しみのかしこまっ!