{"id":13428866,"url":"https://github.com/kohesive/injekt","last_synced_at":"2025-03-16T02:30:39.072Z","repository":{"id":35804295,"uuid":"40086213","full_name":"kohesive/injekt","owner":"kohesive","description":"Dependency Injection for Kotlin","archived":true,"fork":false,"pushed_at":"2019-09-06T13:27:42.000Z","size":302,"stargazers_count":235,"open_issues_count":15,"forks_count":19,"subscribers_count":11,"default_branch":"master","last_synced_at":"2024-10-29T20:24:55.233Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kohesive.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-08-02T16:57:00.000Z","updated_at":"2024-09-23T14:30:44.000Z","dependencies_parsed_at":"2022-09-09T01:52:55.806Z","dependency_job_id":null,"html_url":"https://github.com/kohesive/injekt","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kohesive%2Finjekt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kohesive%2Finjekt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kohesive%2Finjekt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kohesive%2Finjekt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kohesive","download_url":"https://codeload.github.com/kohesive/injekt/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243815603,"owners_count":20352195,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T01:01:07.289Z","updated_at":"2025-03-16T02:30:38.687Z","avatar_url":"https://github.com/kohesive.png","language":"Kotlin","funding_links":[],"categories":["Libraries","开源库和框架","Kotlin","IoC"],"sub_categories":["依赖注入","Spring Cloud框架"],"readme":"[![Kotlin](https://img.shields.io/badge/kotlin-1.0.2-blue.svg)](http://kotlinlang.org) [![Maven Version](https://img.shields.io/maven-central/v/uy.kohesive.injekt/injekt-core.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22uy.kohesive.injekt%22) [![CircleCI branch](https://img.shields.io/circleci/project/kohesive/injekt/master.svg)](https://circleci.com/gh/kohesive/injekt/tree/master) [![Issues](https://img.shields.io/github/issues/kohesive/injekt.svg)](https://github.com/kohesive/injekt/issues?q=is%3Aopen) [![DUB](https://img.shields.io/dub/l/vibe-d.svg)](https://github.com/kohesive/injekt/blob/master/LICENSE) [![Kotlin Slack](https://img.shields.io/badge/chat-kotlin%20slack-orange.svg)](http://kotlinslackin.herokuapp.com)\n\n# Notice:  Kodein and Injekt, much of the same\n\nSince Injekt and Kodein both ended up in a very similar implementation (object registry approach to injection), it makes little sense in having two flavours of the same library for Kotlin.  Therefore Injekt is deferring to Kodein.  Since Injekt has no known bugs, there is no fear in continuing to use it (and I will fix anyting that shows up), but for additional functionality see Kodein instead.  \n\nLibraries such as Klutter will create Kodein modules for their injection modules, same for Kovert.  And Typesafe configuration injection from Klutter will also be ported over to Kodein for future releases.\n\nKodein:  https://github.com/SalomonBrys/Kodein\n\n# Injekt\n\nInjekt gives you crazy easy **Dependency Injection** in Kotlin.  Although you can probably use it in other JVM languages if you are feeling lucky.\n\nInjekt is NOT inversion of control.  It is NOT some mystical class file manipulator.  It is NOT complex.  But it IS powerful.\n\nInjekt can also load, bind to objects, and inject configuration using Typesafe Config.  Read more in the [injekt-config-typesafe module](https://github.com/kohesive/klutter/tree/master/config-typesafe).\n\n## Quick Samples\n\nSimply register singletons (non-lazy), or factories (lazy) and then inject that values either directly or using Kotlin delegates.  For example:\n\nEarly in your program (for modules see [Injekt \"Main\"](#injekt-main) and [Packaged Injektables](#packaged-injektables) below):\n```kotlin\nInjekt.addSingleton(HikariDataSource(HikariConfig(\"some/path/hikari.properties\")) as DataSource)\nInjekt.addSingletonFactory { PeopleService() }\nInjekt.addFactory { ComplexParser.loadFromConfig(\"some/path/parsing.conf\") }\n```\n\nWhere `PeopleService` has default value for constructor parameter that receives the value from Injekt:\n```kotlin\nclass PeopleService(db: DataSource = Injekt.get()) { ... }\n```\n\nThen you can inject `PeopleService` into your code whenever you want to use it:\n\n```kotlin\nclass PeopleController {\n    fun putPerson(person: Person) {\n        val peopelSrv: PeopleService = Injekt.get()\n        // ...\n    }\n}\n```\n\nAnd when injecting into a property you can use the same style, or instead use delegates:\n\n```kotlin\nclass PeopleController {\n    val peopelSrv: PeopleService by injectLazy() // or injectValue() if immediate\n    \n    fun putPerson(person: Person) {\n        // ...\n    }\n}\n```\n\nSince `PeopleService` uses default values in the constructor, you can easily override in tests without requiring an injection model:\n```kotlin\nclass TestPeopleService {\n    companion object {\n        lateinit var db: DataSource\n\n        @JvmStatic @BeforeClass fun setupTests() {\n            db = HikariDataSource(HikariConfig().apply {\n                jdbcUrl = \"jdbc:h2:mem:test\"\n            })\n        }\n    }\n\n    fun testPeopleService() {\n        val people = PeopleService(db) // no injection required\n        // ...\n    }\n}\n```\n\n## Gradle / Maven Dependnecy\n\nInclude the dependency in your Gradle / Maven projects, ones that have Kotlin configured for Kotlin 1.0\n\n**Gradle:**\n```\ncompile \"uy.kohesive.injekt:injekt-core:1.16.+\"\n```\n\n**Maven:**\n```\n\u003cdependency\u003e\n    \u003cgroupId\u003euy.kohesive.injekt\u003c/groupId\u003e\n    \u003cartifactId\u003einjekt-core\u003c/artifactId\u003e\n    \u003cversion\u003e[1.16.0,1.17.0)\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nIt is recommended you set your IDE to auto import `*` for Injekt packages (if your IDE supports such a feature):\n\n```\nimport uy.kohesive.injekt.*\nimport uy.kohesive.injekt.api.*\n```\n\n## Injekt \"Main\"\n\nAt the earliest point in your application startup, you register singletons, factories and your logging factories.  For the simplest version of this process, you can use the `InjektModule` on an object or companion object (from [Injekt Examples](https://github.com/kohesive/injekt/blob/master/core/src/example/kotlin/uy/kohesive/injekt/example/MyApp.kt))\n\n```kotlin\nclass MyApp {\n    companion object : InjektMain() {\n        // my app starts here with a static main()\n        @JvmStatic public fun main(args: Array\u003cString\u003e) {\n            MyApp().run()\n        }\n\n        // the InjektModule() will call me back here on a method I override.  And all my functions for registration are\n        // easy to find on the receiver class\n        override fun InjektRegistrar.registerInjectables() {\n            // let's setup my logger first\n            addLoggerFactory({ byName -\u003e LoggerFactory.getLogger(byName) }, { byClass -\u003e LoggerFactory.getLogger(byClass) })\n\n            // now some singletons\n            addSingleton(HttpServerConfig(\"0.0.0.0\", 8080, 16))\n            addSingleton(DatabaseConnectionConfig(\"db.myhost.com\", \"sa\", \"sillycat\"))\n\n            // or a lazy singleton factory\n            addSingletonFactory { DontCreateUntilWeNeedYa() }\n\n            // or lets only have one database connection per thread, basically a singleton per thread\n            addPerThreadFactory { JdbcDatabaseConnection(Injekt.get()) }  // wow, nested inections!!!\n\n            // or give me a new one each time it is injected\n            addFactory { LazyDazy() }\n\n            // and we also have factories that use a key (or single parameter) to return an instance\n            val pets = listOf(NamedPet(\"Bongo\"), NamedPet(\"Dancer\"), NamedPet(\"Cheetah\")).map { it.name to it}.toMap()\n            addPerKeyFactory { petName: String -\u003e pets.get(petName)!! }\n\n            // use prebuilt injectable packages\n            importModule(AmazonS3InjektModule)\n        }\n    }\n\n    ...\n}\n```\n\nAnd once they are registered, anything else in the system can access them, for example as class properties they can be injected using delegates:\n\n```kotlin\n    val log: Logger by injectLogger()\n    val laziest: LazyDazy by injectLazy()\n    val lessLazy: LazyDazy by injectValue()\n```\n\nor directly as assignments both as property declarations and local assignemtns:\n\n```kotlin\n    val notLazy1: LazyDazy = Injekt.get()\n    val notLazy2 = Injekt.get\u003cLazyDazy\u003e()\n```\n\nAnd they can be used in constructors and methods as default parameters:\n\n```kotlin\n    public fun foo(dbConnectParms: DatabaseConfig = Injekt.get()) { ... }\n```\n\nAnd since we have registered in the first example a mix of types, including thread specific injections and key/parameter based, here they are in action:\n\n```kotlin\n public fun run() {\n        // even local variables can be injected, or rather \"got\"\n        val something = Injekt.get\u003cDontCreateUntilWeNeedYa\u003e()\n        startHttpServer()\n    }\n\n    // and we can inject into methods by using Kotlin default parameters\n    public fun startHttpServer(httpCfg: HttpServerConfig = Injekt.get()) {\n        log.debug(\"HTTP Server starting on ${httpCfg.host}:${httpCfg.port}\")\n        HttpServer(httpCfg.host, httpCfg.port).withThreads(httpCfg.workerThreads).handleRequest { context -\u003e\n            val db: JdbcDatabaseConnection = Injekt.get()    // we have a connection per thread now!\n\n            if (context.params.containsKey(\"pet\")) {\n                // inject from a factory that requires a key / parameter\n                val pet: NamedPet = Injekt.get(context.params.get(\"pet\")!!)\n                // or other form without reified parameters\n                val pet2 = Injekt.get\u003cNamedPet\u003e(context.params.get(\"pet\")!!)\n            }\n        }\n    }\n```\n\n## Packaged Injektables\n\nNow that you have mastered injections, let's make modules of our application provide their own injectable items.  Say our Amazon AWS helper module has a properly configured credential provider chain, and can make clients for us nicely.  It is best to have that module decide the construction and make it available to other modules.  And it's easy.  Create an object that extends `InjektModule` and then it is pretty much the same as before:\n\n```kotlin\npublic object AmazonS3InjektModule : InjektModule {\n    override fun InjektRegistrar.registerInjectables() {\n        addSingletonFactory { AmazonS3Client(defaultCredentialsProviderChain()) }\n    }\n}\n```\n\nThe only difference between an `InjektMain` and `InjektModule` object is that a `InjektMain` is automatically called to initialize, whereas an `InjektModule` does not do anything until it is imported by another `InjektMain` or `InjektModule`.  Using `InjektModule` is simple, go back to the first example at the top of this page and you will see it imported with a simple\n\n```kotlin\n // use prebuilt package\nimportModule(AmazonS3InjektModule)\n```\n\nNote:  if you extend `InjektMain` you are also a module that can be imported.  \n\nNote:  If you use scopes (see `InjektScope` and `InjektScopeMain` then you should be aware that using `InjektMain` points at the global scope always).\n\n## One Instance Per-Thread Factories -- a tip\n\nWhen using a factory that is per-thread, be sure not to pass the object to other threads if you really intend for them to be isolated.  Lookup of such objects is from ThreadLocal storage and fast, so it is better to keep these objects for shorter durations or in situations guaranteed to stay on the same thread as that which retrieved the object.\n\n## Injecting Configuration with Typesafe Config\n\nInjekt + Klutter library can also load, bind to objects, and inject configuration using Typesafe Config.  Read more in the [klutter/config-typesafe](https://github.com/kohesive/klutter/tree/master/config-typesafe) module.\n\n## Generics, Erased Type and Injection\n\nIt is best to use type inference for all methods in Injekt to let the full type information come through to the system.  If the compiler can determine\nthe types, it is likely the full generic information is captured.  But if a value passed to Injekt does not have that information available at the calling\nsite, then you can specify the full generic type as a type parameter to the method, for example `Injekt.get\u003cMyFullType\u003cWithGenerics\u003cAndMore\u003e\u003e\u003e()`.  Methods also\naccept a `TypeReference\u003cT\u003e` which can be passed around as a means to parameterize and send type information through the system.  The helper method `fullType\u003cT\u003e()`\ncan create a `TypeReference` for you, for example `Injekt.get(fullType\u003cMyFullType\u003cWithGenerics\u003e\u003e())`.\n\nIt is preferable that you use (in priority order):\n\n* The inferred forms of Injekt functions that infer their type from the surrounding expression.  Do not provide a type unless something breaks.\n* Or if you want to change the type recognized by Injekt, use an explicit generic type for the function `someFunction\u003cMyType\u003e()`.\n* If those are not to your liking, then use the `TypeReference` version passing in a `typeRef\u003cT\u003e()` generated `TypeReference` ... this is your fallback in cases where things need full and exact control.\n\nBy doing this, you prevent surprises because you are in full control and it is obvious what types are expected.\n\n## Scopes\n\nInjekt allows manual scoping of instances into separate Injekt registries.  The global registry, available through the `Injekt` variable is just one scope that is pre-created for you.  You can also create new ones:\n\n```\nval myLocalScope: InjektScope = InjektScope(DefaultRegistrar())\n```\n\nThis makes a standalone scope that has no relationship to the global or to others.  \n\nBut then you can link scopes by creating factories in the new scope that delegate some of the instance creation to another scope, or the global `Injekt` scope.  For example:\n\n```\n// delegate some factories to global Injekt instance\nmyLocalScope.addSingletonFactory { Injekt.get\u003cSomeSingletonClass\u003e() }\nmyLocalScope.addFactory { Injekt.get\u003cSomeMultiValueClass\u003e() }\n```\n\nWhen delegating factories such as this, any multi-value instances will not be cached by any scope since those factories create new instances on every call.  For singletons and keyed factories the objects are cached and a reference to those objects will exist in both the local and delegated scopes for any instances requested during its lifecycle.  \n\nYou can also just use multiple scopes independently without linking or delegation.  Fetch some instances from a local scope, others from the global.  But you must use each scope independently and be careful of accidentally using the `Injekt` global variable when not intended.\n\nIf you have common factories needed in local scopes, you can easily create a descendent of `InjektScope` that registers these during its construction.  \n\n```\nclass MyActivityScope: InjektScope(DefaultRegistrar()) {\n    init {\n        // override with local value\n        addSingletonFactory { SomeSingletonClass() }\n        // import other registrations from defined modules\n        importModule(OtherModuleWithPrepackagedInjektions)\n        // delegate to global scope:\n        addSingletonFactory { Injekt.get\u003cSomeOtherSingleton\u003e() }\n    }\n}\n\n// then in each place you want a local scope\nval localScope = MyActivityScope()\n\n// later use the scope\nval singly: SomeSingletonClass = localScope.get()\nval other: SomeOtherSingleton = localScope.get()\n```\n\nOr using the same model as `InjektMain` create a descendent of `InjektScopedMain` that overrides function `fun InjektRegistrar.registerInjectables() { ... }`, if you prefer to be consistent with modules.  For example:\n\n```\nclass MyActivityModule: InjektScopedMain(InjektScope(DefaultRegistrar())) {\n    override fun InjektRegistrar.registerInjectables() {\n        // override with local value\n        addSingletonFactory { NotLazy(\"Freddy\") }\n        // import other registrations from defined modules\n        importModule(OtherModuleWithPrepackagedInjektions)\n        // delegate to global scope:\n        addSingletonFactory { Injekt.get\u003cSomeOtherSingleton\u003e() }\n    }\n}\n\n// then in each place you want a local scope\nval localScope = MyActivityModule().scope\n```\n\nAnd you can still use delegated properties, as long as the scope is declared before use in the delegate:\n\n```\nval myProp: SomeClass by localScope.injectValue()\n```\n\nYou can use the `LocalScoped` base class to have local versions of `injectValue()` and `injectLazy()` to make it more convenient when injecting members (see [code for `LocalScoped`](https://github.com/kohesive/injekt/blob/master/api/src/main/kotlin/uy/kohesive/injekt/api/Scope.kt#L61-L79)).  This way your syntax stays consistent (see [example in tests](https://github.com/kohesive/injekt/blob/master/core/src/test/kotlin/uy/kohesive/injekt/TestInjektion.kt#L202-L247)).\n\nTo clear a local scope, drop your reference to the scope and it will garabage collect away.  There is no explicit clear method.\n\nFor more advanced, and more automatic scope linking / delegation / inheritance, please see issue [#31](https://github.com/kohesive/injekt/issues/31) and provide comments.  For propagating a scope into other classes injected into the class declaring the local scope, see the test case referenced from [#32](https://github.com/kohesive/injekt/issues/32)\n\n## Coming soon... (RoadMap)\n\n* Linked Scopes, see [#31](https://github.com/kohesive/injekt/issues/31)\n* Scoped factories, see [#32](https://github.com/kohesive/injekt/issues/32)\n* Materializing object graphs without explicit calls to Injekt\n* Tell me what you would like to see, add Issues here in Github with requests.\n\n## Recommended libraries:\n\nOther libraries that we recommend a building blocks for Kotlin applications:\n\n* [Kovenant](http://kovenant.komponents.nl) - promises for Kotlin, easy, fun, and async! (JVM / Android)\n* [Klutter](http://github.com/kohesive/klutter) - simple, small, useful things for Kotlin\n\n## With help from...\n\n[![Collokia Logo](https://www.collokia.com/images/collokia-logo-210x75.png)](https://www.collokia.com)\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkohesive%2Finjekt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkohesive%2Finjekt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkohesive%2Finjekt/lists"}