Kotlin-függőségi injekció Kodeinnal
1. Áttekintés
Ebben a cikkben bemutatjuk a Kodeint - egy tiszta Kotlin-függőségi injekció (DI) keretrendszert -, és összehasonlítjuk más népszerű DI-keretrendszerekkel.
2. Függőség
Először tegyük hozzá a Kodein-függőséget a sajátunkhoz pom.xml:
com.github.salomonbrys.kodein kodein 4.1.0
Felhívjuk figyelmét, hogy a legújabb elérhető verzió elérhető a Maven Central vagy a jCenter webhelyen.
3. Konfiguráció
Az alábbi modellt használjuk a Kodein-alapú konfiguráció bemutatására:
osztály Vezérlő (privát val service: Service) osztály Service (private val dao: Dao, private val tag: String) interfész Dao osztály JdbcDao: Dao osztály MongoDao: Dao
4. Kötési típusok
A Kodein keretrendszer különféle kötéstípusokat kínál. Vizsgáljuk meg közelebbről, hogyan működnek és hogyan használják őket.
4.1. Szingli
Val vel Szingli kötés, a cél babot lustán példányosítják az első hozzáféréskor és minden további kérésre felhasználják:
var létre = hamis; val kodein = Kodein {bind () singletonnal {MongoDao ()}} assertThat (létrehozva) .isFalse () val dao1: Dao = kodein.instance () assertThat (létrehozva) .isFalse () val dao2: Dao = kodein.instance () assertThat (dao1) .isSameAs (dao2)
Megjegyzés: használhatjuk Kodein.instance () statikus változótípus alapján célzottan kezelt babok lekérésére.
4.2. Vágyakozó Singleton
Ez hasonló a Szingli kötés. Az egyetlen különbség az az inicializálási blokkot lelkesen hívják:
var létre = hamis; val kodein = Kodein {bind () singletonnal {MongoDao ()}} assertThat (létrehozva) .isTrue () val dao1: Dao = kodein.instance () val dao2: Dao = kodein.instance () assertThat (dao1) .isSameAs (dao2)
4.3. Gyár
Val vel Gyár kötés, az inicializáló blokk argumentumot kap, és minden alkalommal új tárgyat adnak vissza belőle:
val kodein = Kodein {bind () singletonnal {MongoDao ()} bind () gyárilag {tag: String -> Service (instance (), tag)}} val service1: Service = kodein.with ("myTag"). instance () val service2: Service = kodein.with ("myTag"). instance () assertThat (service1) .isNotSameAs (service2)
Megjegyzés: használhatjuk Kodein.instance () a tranzitív függőségek konfigurálásához.
4.4. Multiton
Multiton a kötés nagyon hasonlít a Gyár kötés. Az egyetlen különbség az ugyanazt az objektumot adja vissza ugyanazon argumentumért a következő hívások során:
val kodein = Kodein {bind () singletonnal {MongoDao ()} bind () 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. Szolgáltató
Ez egy no-arg Gyár kötés:
val kodein = Kodein {bind () {MongoDao ()}} szolgáltatóval val dao1: Dao = kodein.instance () val dao2: Dao = kodein.instance () assertThat (dao1) .isNotSameAs (dao2)
4.6. Példa
Tudunk regisztráljon egy előre konfigurált babpéldányt a tartályban:
val dao = MongoDao () val kodein = Kodein {bind () példánnyal (dao)} val fromContainer: Dao = kodein.instance () assertThat (dao) .isSameAs (fromContainer)
4.7. Címkézés
Azt is megtehetjük regisztráljon több, azonos típusú babot különböző címkék alatt:
val kodein = Kodein {kötés ("dao1") szingulettel {MongoDao ()} kötés ("dao2") szingulettel {MongoDao ()}} val dao1: Dao = kodein.instance ("dao1") val dao2: Dao = kodein.instance ("dao2") assertThat (dao1) .isNotSameAs (dao2)
4.8. Állandó
Ez szintaktikus cukor a címkézett kötéssel szemben, és feltételezzük konfigurációs állandókhoz használható - egyszerű típusok öröklés nélkül:
val kodein = Kodein {állandó ("varázslat") 42-vel} val fromContainer: Int = kodein.instance ("magic") assertThat (fromContainer) .isEqualTo (42)
5. Kötések elválasztása
A Kodein lehetővé teszi számunkra, hogy a babokat külön blokkokban konfiguráljuk és kombináljuk.
5.1. Modulok
Tudunk csoportosítsa az összetevőket bizonyos szempontok szerint - például az összes adatmegmaradással kapcsolatos osztály - és a blokkokat kombinálva készítsen egy kapott konténert:
val jdbcModule = Kodein.Module {bind () singletonnal {JdbcDao ()}} val kodein = Kodein {import (jdbcModule) bind () singletonnal {Controller (instance ())} bind () singleton {Service (instance ( ), "myService")}} val dao: Dao = kodein.instance () assertThat (dao) .isInstanceOf (JdbcDao :: class.java)
Megjegyzés: mivel a modulok kötelező érvényű szabályokat tartalmaznak, a célbabok újra létrejönnek, ha ugyanazt a modult több Kodein példányban használják.
5.2. Fogalmazás
Kiterjeszthetjük az egyik Kodein példányt a másikról - ez lehetővé teszi számunkra a bab újrafelhasználását:
val persistenceContainer = Kodein {bind () singletonnal {MongoDao ()}} val serviceContainer = Kodein {extension (persistenceContainer) bind () singletonnal {Service (instance (), "myService")}} val fromPersistence: Dao = persistenceContainer. instance () val fromService: Dao = serviceContainer.instance () assertThat (fromPersistence) .isSameAs (fromService)
5.3. Felülbíráló
Felülírhatjuk a kötéseket - ez hasznos lehet a teszteléshez:
class InMemoryDao: Dao val commonModule = Kodein.Module {bind () singletonnal {MongoDao ()} bind () singleton {Service (instance (), "myService")}} val testContainer = Kodein {import (commonModule) bind ( felülbírálja = igaz) szingulettel {InMemoryDao ()}} val dao: Dao = testContainer.instance () assertThat (dao) .isInstanceOf (InMemoryDao :: class.java)
6. Több kötés
Konfigurálhatjuk több, ugyanazzal a közös (szuper-) típussal rendelkező bab a tartályban:
val kodein = Kodein {bind () from setBinding () bind (). inSet () singletonnal {MongoDao ()} bind (). inSet () singletonnal {JdbcDao ()}} val daos: Set = kodein.instance ( ) assertThat (daos.map {it.javaClass as Class}) .conconOnly (MongoDao :: class.java, JdbcDao :: class.java)
7. Injektor
Alkalmazási kódunk nem ismerte a Kodeint az összes példában, amelyet korábban használtunk - rendszeres konstruktor argumentumokat használt, amelyeket a tároló inicializálása során adtak meg.
A keret azonban lehetővé teszi a függőségek konfigurálásának alternatív módja a delegált tulajdonságok és a Injektorok:
class Controller2 {private val injector = KodeinInjector () val service: Service with injector.instance () fun injectDependencies (kodein: Kodein) = injector.inject (kodein)} val kodein = Kodein {bind () szingulettel {MongoDao ()} bind () singletonnal {Service (instance (), "myService")}} val kontroller = Controller2 () controller.injectDependencies (kodein) assertThat (controller.service) .isNotNull
Más szavakkal, egy tartományosztály egy injektoron keresztül határozza meg a függőségeket, és egy adott tárolóból szerzi be azokat. Ez a megközelítés olyan speciális környezetekben hasznos, mint az Android.
8. A Kodein használata Android rendszerrel
Az Android rendszerben a Kodein tároló egy egyedi konfigurációban van Alkalmazás osztályba, később pedig a Kontextus példa. Feltételezzük, hogy minden komponens (tevékenység, töredék, szolgáltatás, műsorszóró vevő) kiterjesztésre kerül a hasonló segédprogram osztályokból KodeinActivity és KodeinTöredék:
class MyActivity: Activity (), KodeinInjected {override val injector = KodeinInjector () val random: Véletlenszerű példányonként () felülírja a fun onCreate (savedInstanceState: Bundle?) {inject (appKodein ())}}
9. Elemzés
Ebben a részben meglátjuk, hogy viszonyul a Kodein a népszerű DI keretrendszerekhez.
9.1. Tavaszi keret
A Spring Framework sokkal funkciógazdagabb, mint a Kodein. Például a Tavasznak van egy nagyon kényelmes alkatrész-szkennelési lehetőség. Amikor osztályainkat olyan jelölésekkel jelöljük, mint például @Összetevő, @Szolgáltatás, és @Nevezett, a komponens szkennelés automatikusan felveszi ezeket az osztályokat a tároló inicializálása során.
A tavasznak is van hatékony meta-programozási kiterjesztési pontok, BeanPostProcessor és BeanFactoryPostProcessor, ami kulcsfontosságú lehet egy konfigurált alkalmazás adott környezethez történő adaptálásakor.
Végül a Spring nyújt néhányat a tetejére épített kényelmes technológiák, beleértve az AOP-t, a Tranzakciókat, a Test Framework-et és még sokan mások. Ha ezeket használni akarjuk, érdemes ragaszkodni a tavaszi IoC tartályhoz.
9.2. 2. tőr
A Dagger 2 keretrendszer az nem olyan gazdag funkciókban, mint a Spring Framework, de népszerű az Android fejlesztésében sebessége (Java kódot generál, amely végrehajtja az injekciót és csak futás közben hajtja végre) és mérete miatt.
Hasonlítsuk össze a könyvtárak metódusszámát és méretét:
Kodein:Vegye figyelembe, hogy a kotlin-stdlib ezeknek a számoknak a nagy részét a függőség teszi ki. Ha kizárjuk, 1282 metódust és 244 KB DEX méretet kapunk.
2. tőr:
Láthatjuk, hogy a Dagger 2 keretrendszer sokkal kevesebb metódust ad hozzá, és a JAR fájlja kisebb.
Ami a felhasználást illeti - nagyon hasonló abban, hogy a felhasználói kód függőségeket konfigurál (keresztül Injektor a Kodein és a JSR-330 annotációkban a Dagger 2-ben), majd később egyetlen módszerrel hívják őket be.
A Dagger 2 legfontosabb jellemzője azonban, hogy az validálja a függőségi grafikont fordításkor, így konfigurációs hiba esetén nem teszi lehetővé az alkalmazás lefordítását.
10. Következtetés
Most már tudjuk, hogyan kell használni a Kodeint a függőség injektálásához, milyen konfigurációs lehetőségeket kínál, és hogyan hasonlítható össze néhány más népszerű DI keretrendszerrel. Azonban rajtad múlik, hogy felhasználja-e valódi projektekben.
Mint mindig, a fenti minták forráskódja a GitHub oldalon található.