Injekcia závislosti na kotline s kodeínom
1. Prehľad
V tomto článku si predstavíme Kodein - čistý rámec pre vstrekovanie závislostí Kotlin (DI) - a porovnáme ho s inými populárnymi rámcami DI.
2. Závislosť
Najskôr pridajme závislosť od kodeínu do našej pom.xml:
com.github.salomonbrys.kodein kodein 4.1.0
Upozorňujeme, že najnovšia dostupná verzia je k dispozícii buď v Maven Central, alebo jCenter.
3. Konfigurácia
Nižšie uvedený model použijeme na ilustráciu konfigurácie založenej na kodeíne:
trieda Controller (private val service: Service) class Service (private val dao: Dao, private val tag: String) interface Dao class JdbcDao: Dao class MongoDao: Dao
4. Typy väzieb
Kodein framework ponúka rôzne typy väzieb. Poďme sa bližšie pozrieť na to, ako fungujú a ako ich používať.
4.1. Singleton
S Singleton viazanie, cieľová fazuľa je lenivo inštancovaná pri prvom prístupe a znova použité pri všetkých ďalších požiadavkách:
var created = false; val kodein = Kodein {bind () so singletonom {MongoDao ()}} assertThat (vytvorené) .isFalse () val dao1: Dao = kodein.instance () assertThat (vytvorené) .isFalse () val dao2: Dao = kodein.instance () assertThat (dao1) .isSameAs (dao2)
Poznámka: môžeme použiť Kodein.instance () na získanie cieľovo riadených bôbov na základe typu statickej premennej.
4.2. Dychtivý Singleton
Je to podobné ako v prípade Singleton viazanie. Rozdiel je iba v tom inicializačný blok sa volá nedočkavo:
var created = false; val kodein = Kodein {bind () so singletonom {MongoDao ()}} assertThat (vytvorený) .isTrue () val dao1: Dao = kodein.instance () val dao2: Dao = kodein.instance () assertThat (dao1) .isSameAs (dao2)
4.3. Továreň
S Továreň väzba, inicializačný blok prijme argument a zakaždým sa z neho vráti nový objekt:
val kodein = Kodein {bind () so singletonom {MongoDao ()} bind () s továrňou {tag: String -> Service (instance (), tag)}} val service1: Service = kodein.with ("myTag"). instance () val service2: Service = kodein.with ("myTag"). instance () assertThat (service1) .isNotSameAs (service2)
Poznámka: môžeme použiť Kodein.instance () na konfiguráciu tranzitívnych závislostí.
4.4. Multiton
Multiton väzba je veľmi podobná Továreň viazanie. Rozdiel je iba v tom ten istý objekt sa vráti pre ten istý argument v ďalších hovoroch:
val kodein = Kodein {bind () so singletonom {MongoDao ()} bind () s multiton {tag: String -> Service (instance (), tag)}} val service1: Service = kodein.with ("myTag"). instance () val service2: Service = kodein.with ("myTag"). instance () assertThat (service1) .isSameAs (service2)
4.5. Poskytovateľ
Toto nie je arg Továreň väzba:
val kodein = Kodein {bind () s poskytovateľom {MongoDao ()}} val dao1: Dao = kodein.instance () val dao2: Dao = kodein.instance () assertThat (dao1) .isNotSameAs (dao2)
4.6. Inštancia
Môžeme zaregistrujte predkonfigurovanú inštanciu fazule v kontajneri:
val dao = MongoDao () val kodein = Kodein {bind () s inštanciou (dao)} val fromContainer: Dao = kodein.instance () assertThat (dao) .isSameAs (fromContainer)
4.7. Označovanie
Môžeme tiež zaregistrujte viac ako jednu fazuľu rovnakého typu pod rôznymi značkami:
val kodein = Kodein {bind ("dao1") so singletonom {MongoDao ()} bind ("dao2") so singletonom {MongoDao ()}} val dao1: Dao = kodein.instance ("dao1") val dao2: Dao = kodein.instance ("dao2") assertThat (dao1) .isNotSameAs (dao2)
4.8. Neustále
Toto je syntaktický cukor cez značenú väzbu a predpokladá sa ktoré sa majú použiť pre konfiguračné konštanty - jednoduché typy bez dedenia:
val kodein = Kodein {konštanta ("mágia") s 42} val zContainer: Int = kodein.instance ("magic") assertThat (fromContainer) .isEqualTo (42)
5. Separácia väzieb
Kodein nám umožňuje konfigurovať fazuľa v samostatných blokoch a kombinovať ich.
5.1. Moduly
Môžeme zoskupiť komponenty podľa konkrétnych kritérií - napríklad všetky triedy týkajúce sa vytrvalosti údajov - a skombinujte bloky tak, aby ste vytvorili výsledný kontajner:
val jdbcModule = Kodein.Module {bind () so singletonom {JdbcDao ()}} val kodein = Kodein {import (jdbcModule) bind () so singletom {Controller (instance ())} bind () so singletonom {Service (instance ( ), "myService")}} val dao: Dao = kodein.instance () assertThat (dao) .isInstanceOf (JdbcDao :: class.java)
Poznámka: pretože moduly obsahujú pravidlá viazania, cieľové fazule sa znovu vytvoria, keď sa ten istý modul použije vo viacerých inštanciách Kodein.
5.2. Zloženie
Môžeme rozšíriť jednu inštanciu Kodein z druhej - to nám umožní znovu použiť fazuľa:
val persistenceContainer = Kodein {bind () so singletonom {MongoDao ()}} val serviceContainer = Kodein {extend (persistenceContainer) bind () s singleton {Service (instance (), "myService")}} val fromPersistence: Dao = persistenceContainer. instance () val fromService: Dao = serviceContainer.instance () assertThat (fromPersistence) .isSameAs (fromService)
5.3. Naliehavé
Viazania môžeme prepísať - to môže byť užitočné pri testovaní:
trieda InMemoryDao: Dao val commonModule = Kodein.Module {bind () so singletonom {MongoDao ()} bind () s singleton {Service (instance (), "myService")}} val testContainer = Kodein {import (commonModule) bind ( overrides = true) s singleton {InMemoryDao ()}} val dao: Dao = testContainer.instance () assertThat (dao) .isInstanceOf (InMemoryDao :: class.java)
6. Viacväzby
Môžeme nakonfigurovať viac ako jedna fazuľa rovnakého spoločného (super-) typu v kontajneri:
val kodein = Kodein {bind () from setBinding () bind (). inSet () with singleton {MongoDao ()} bind (). inSet () with singleton {JdbcDao ()}} val daos: Set = kodein.instance ( ) assertThat (daos.map {it.javaClass as Class}) .containsOnly (MongoDao :: class.java, JdbcDao :: class.java)
7. Injektor
Náš kód aplikácie nevedel o Kodeine vo všetkých príkladoch, ktoré sme používali predtým - používal bežné argumenty konštruktora, ktoré boli poskytnuté počas inicializácie kontajnera.
Rámec to však umožňuje - alternatívny spôsob konfigurácie závislostí prostredníctvom delegovaných vlastností a Injektory:
trieda Controller2 {private val injector = KodeinInjector () val služba: Service by injector.instance () fun injectDependencies (kodein: Kodein) = injector.inject (kodein)} val kodein = Kodein {bind () with singleton {MongoDao ()} bind () s singleton {Service (instance (), "myService")}} val controller = Controller2 () controller.injectDependencies (kodein) assertThat (controller.service) .isNotNull
Inými slovami, doménová trieda definuje závislosti prostredníctvom injektora a získava ich z daného kontajnera. Takýto prístup je užitočný v konkrétnych prostrediach, ako je Android.
8. Používanie Kodeinu so systémom Android
V systéme Android je kontajner Kodein nakonfigurovaný ako obyčajný Aplikácia triedy a neskôr sa viaže na Kontext inštancia. Predpokladá sa, že všetky komponenty (aktivity, fragmenty, služby, rozhlasové prijímače) sú rozšírené z tried nástrojov, ako sú KodeinActivity a KodeinFragment:
trieda MyActivity: Activity (), KodeinInjected {override val injector = KodeinInjector () val random: Random by instance () override fun onCreate (savedInstanceState: Bundle?) {inject (appKodein ())}}
9. Analýza
V tejto časti uvidíme, ako si Kodein porovnáva s populárnymi DI frameworkmi.
9.1. Jarný rámec
Jarný rámec je oveľa bohatší na funkcie ako Kodein. Napríklad jar má veľmi pohodlné skenovanie komponentov. Keď označíme naše triedy konkrétnymi anotáciami ako @ Komponent, @Službaa @ Menovaný, skenovanie komponentov automaticky vyzdvihne tieto triedy počas inicializácie kontajnera.
Jar má tiež výkonné body rozšírenia metaprogramovania, BeanPostProcessor a BeanFactoryPostProcessor, čo môže byť rozhodujúce pri prispôsobovaní nakonfigurovanej aplikácie konkrétnemu prostrediu.
Nakoniec Spring poskytuje niektoré z nich na tom postavené pohodlné technológie, vrátane AOP, transakcií, testovacieho rámca a mnohých ďalších. Ak ich chceme použiť, stojí za to zostať pri kontajneri Spring IoC.
9.2. Dýka 2
Rámec Dagger 2 je nie tak bohatý na funkcie ako Spring Framework, ale je populárny vo vývoji systému Android kvôli svojej rýchlosti (generuje kód Java, ktorý vykonáva vstrekovanie a iba ho vykonáva za behu) a veľkosti.
Porovnajme počty a veľkosti metód knižníc:
Kodein:Všimnite si, že kotlin-stdlib závislosť predstavuje väčšinu týchto čísel. Keď to vylúčime, dostaneme 1282 metód a veľkosť DEX 244 KB.
Dýka 2:
Vidíme, že rámec Dagger 2 pridáva oveľa menej metód a jeho súbor JAR je menší.
Pokiaľ ide o použitie - je to veľmi podobné v tom, že používateľský kód konfiguruje závislosti (prostredníctvom Injektor v anotáciách Kodein a JSR-330 v Dagger 2) a neskôr ich injektuje prostredníctvom jedného volania metódy.
Kľúčovou vlastnosťou hry Dagger 2 je však to, že je overuje závislostný graf v čase kompilácie, takže neumožní kompiláciu aplikácie, ak dôjde k chybe konfigurácie.
10. Záver
Teraz vieme, ako používať Kodein na vkladanie závislostí, aké možnosti konfigurácie poskytuje a ako je v porovnaní s niekoľkými ďalšími populárnymi rámcami DI. Je však na vašom rozhodnutí, či ho použijete v reálnych projektoch.
Zdrojový kód vyššie uvedených vzorov nájdete ako vždy na serveri GitHub.