All Projects → evant → Kotlin Inject

evant / Kotlin Inject

Licence: apache-2.0
Dependency injection lib for kotlin

Programming Languages

kotlin
9241 projects

Projects that are alternatives of or similar to Kotlin Inject

Arouter
💪 A framework for assisting in the renovation of Android componentization (帮助 Android App 进行组件化改造的路由框架)
Stars: ✭ 13,587 (+5378.63%)
Mutual labels:  dependency-injection
Pimple
A small PHP dependency injection container
Stars: ✭ 2,491 (+904.44%)
Mutual labels:  dependency-injection
Ulfberht
🗡️ A small but powerful & opinionated DI library. Written in Kotlin, and powered by annotation processing.
Stars: ✭ 234 (-5.65%)
Mutual labels:  dependency-injection
Reto
Flexible and efficient React Store with hooks.
Stars: ✭ 194 (-21.77%)
Mutual labels:  dependency-injection
Inversify Express Example
The official express + inversify+ inversify-express-utils examples
Stars: ✭ 210 (-15.32%)
Mutual labels:  dependency-injection
Angular Awesome List
Список ресурсов по Angular на русском
Stars: ✭ 224 (-9.68%)
Mutual labels:  dependency-injection
Activej
ActiveJ is an alternative Java platform built from the ground up. ActiveJ redefines web, high load, and cloud programming in Java, featuring ultimate performance and scalability!
Stars: ✭ 183 (-26.21%)
Mutual labels:  dependency-injection
Movies Kotlin Kata
Katas for practice Kotlin( Coroutines, dataclasses, delegate properties...) Clean Architecture and best practices in Android(DI, Dagger, MVP, Espresso) implemented by Jorge Sánchez (Xurxodev)
Stars: ✭ 240 (-3.23%)
Mutual labels:  dependency-injection
Swinjectstoryboard
Swinject extension for automatic dependency injection via Storyboard
Stars: ✭ 211 (-14.92%)
Mutual labels:  dependency-injection
Invoker
Generic and extensible callable invoker
Stars: ✭ 229 (-7.66%)
Mutual labels:  dependency-injection
Koin Samples
KOIN - a concise and pragmatic dependency injection framework for Kotlin -- #Samples
Stars: ✭ 200 (-19.35%)
Mutual labels:  dependency-injection
Testdeck
Object oriented testing
Stars: ✭ 206 (-16.94%)
Mutual labels:  dependency-injection
Transfuse
💉 Transfuse - A Dependency Injection and Integration framework for Google Android
Stars: ✭ 226 (-8.87%)
Mutual labels:  dependency-injection
Malagu
Malagu is a Serverless First, component-based, platform-independent, progressive application framework based on TypeScript.
Stars: ✭ 184 (-25.81%)
Mutual labels:  dependency-injection
Grafter
Grafter is a library to configure and wire Scala applications
Stars: ✭ 240 (-3.23%)
Mutual labels:  dependency-injection
Reactant
A framework for building React applications
Stars: ✭ 185 (-25.4%)
Mutual labels:  dependency-injection
Coldbox Platform
A modern, fluent and conventions based HMVC framework for ColdFusion (CFML)
Stars: ✭ 220 (-11.29%)
Mutual labels:  dependency-injection
Typhoon Example
An example application built with Typhoon dependency injection framework.
Stars: ✭ 246 (-0.81%)
Mutual labels:  dependency-injection
Grace
Grace is a feature rich dependency injection container library
Stars: ✭ 240 (-3.23%)
Mutual labels:  dependency-injection
Dry System
Organize your code into reusable components
Stars: ✭ 228 (-8.06%)
Mutual labels:  dependency-injection

kotlin-inject

CircleCIMaven Central Sonatype Snapshot

A compile-time dependency injection library for kotlin.

@Component abstract class AppComponent {
    abstract val repo: Repository
    
    @Provides protected fun jsonParser() = JsonParser()

    protected val RealHttp.http: Http
        @Provides get() = this
}

interface Http
@Inject class RealHttp
@Inject class Api(private val http: Http, private val jsonParser: JsonParser)
@Inject class Repository(private val api: Api)
val appComponent = AppComponent::class.create()
val repo = appComponent.repo

Download

plugins {
    id 'org.jetbrains.kotlin.jvm' version '1.4.30'
    id 'org.jetbrains.kotlin.kapt' version '1.4.30'
}

dependencies {
    kapt "me.tatarka.inject:kotlin-inject-compiler-kapt:0.3.1"
    implementation "me.tatarka.inject:kotlin-inject-runtime:0.3.1"
}

Experimental KSP Support

You can use ksp instead of kapt.

settings.gradle

pluginManagement {
    repositories {
        gradlePluginPortal()
        google()
    }
}

build.gradle

plugins {
    id 'org.jetbrains.kotlin.jvm' version '1.4.30'
    id 'com.google.devtools.ksp' version '1.4.30-1.0.0-alpha03'
}

repositories {
    mavenCentral()
    google()
}

dependencies {
    ksp "me.tatarka.inject:kotlin-inject-compiler-ksp:0.3.1"
    implementation "me.tatarka.inject:kotlin-inject-runtime:0.3.1"
}

Usage

Let's go through the above example line-by line and see what it's doing.

@Component abstract class AppComponent {

The building block of kotlin-inject is a component which you declare with an @Component annotation on an abstract class. An implementation of this component will be generated for you.

    abstract val repo: Repository

In you component you can declare abstract read-only properties or functions to return an instance of a given type. This is where the magic happens. kotlin-inject will figure out how to construct that type for you in it's generated implementation. How does it know how to do this? There's a few ways:

    @Provides protected fun jsonParser() = JsonParser()

For external dependencies, you can declare a function or read-only property in the component to create an instance for a certain type. kotlin-inject will use the return type to provide this instance where it is requested.

    protected val RealHttp.http: Http
        @Provides get() = this

You can declare arguments to a providing function/property to help you construct your instance. Here we are taking in an instance of RealHttp and providing it for the interface Http. You can see a little sugar with this as the receiver type for an extension function/property counts as an argument. Another way to write this would be: fun provides(http: RealHttp): Http = http.

@Inject class RealHttp
@Inject class Api(private val http: Http, private val jsonParser: JsonParser)
@Inject class Repository(private val api: Api)

For your own dependencies you can simply annotate the class with @Inject. This will use the primary constructor to create an instance, no other configuration required!

val appComponent = AppComponent::class.create()
val repo = appComponent.repo

Finally, you can create an instance of your component with the generated .create() extension function.

Features

Component Arguments

If you need to pass any instances into your component you can declare them as constructor args. You can then pass them into the generated create function. You can optionally annotate it with @Provides to provide the value to the dependency graph.

@Component abstract class MyComponent(@get:Provides protected val foo: Foo)
MyComponent::class.create(Foo())

If the argument is another component, you can annotate it with @Component and it's dependencies will also be available to the child component. This allows you to compose them into a graph.

@Component abstract class ParentComponent {
    protected fun provideFoo(): Foo = ...
}

@Component abstract class ChildComponent(@Component val parent: ParentComponent) {
    abstract val foo: Foo
}
val parent = ParentComponent::class.create()
val child = ChildComponent::class.create(parent)

Type Alias Support

If you have multiple instances of the same type you want to differentiate, you can use type aliases. They will be treated as separate types for the purposes of injection.

typealias Dep1 = Dep
typealias Dep2 = Dep

@Component abstract class MyComponent {
    @Provides fun dep1(): Dep1 = Dep("one")
    @Provides fun dep2(): Dep2 = Dep("two")

    @Provides fun provides(dep1: Dep1, dep2: Dep2) = Thing(dep1, dep2)
}

@Inject class InjectedClass(dep1: Dep1, dep2: Dep2)

Function Injection

You can also use type aliases to inject into top-level functions. Annotate your function with @Inject and create a type alias with the same name.

typealias myFunction = () -> Unit

@Inject fun myFunction(dep: Dep) {
}

You can then use the type alias anywhere and you will be provided with a function that calls the top-level one with the requested dependencies.

@Inject class MyClass(val myFunction: myFunction)

@Component abstract class MyComponent {
    abstract val myFunction: myFunction
}

You can optionally pass explicit args as the last arguments of the function.

typealias myFunction = (String) -> String

@Inject fun myFunction(dep: Dep, arg: String): String = ...

Scopes

By default kotlin-inject will create a new instance of a dependency each place it's injected. If you want to re-use an instance you can scope it to a component. The instance will live as long as that component does.

First create your scope annotation.

@Scope
@Target(CLASS, FUNCTION, PROPERTY_GETTER)
annotation class MyScope

Then annotate your component with that scope annotation.

@MyScope @Component abstract class MyComponent()

Finally, annotate your provides and @Inject classes with that scope.

@MyScope @Component abstract class MyComponent {
    @MyScope @Provides
    protected fun provideFoo() = ...
}

@MyScope @Inject class Bar()

Component Inheritance

You can define @Provides and scope annotations on an interface or abstract class that's not annotated with @Component. This allows you to have multiple implementations, which is useful for things like testing. For example, you can have an abstract class like

@NetworkScope abstract class NetworkComponent {
    @NetworkScope @Provides
    abstract fun api(): Api 
}

Then you can have multiple implementations

@Component abstract class RealNetworkComponent : NetworkComponent() {
    override fun api(): Api = RealApi()
}

@Component abstract class TestNetworkComponent : NetworkComponent() {
    override fun api(): Api = FakeApi()
}

Then you can provide the abstract class to your app component

@Component abtract class AppComponent(@Component val network: NetworkComponent)

Then in your app you can do

AppComponent::class.create(RealNetworkComponent::class.create())

an in tests you can do

AppComponent::class.create(TestNetworkComponent::class.create())

Multi-bindings

You can collect multiple bindings into a Map or Set by using the @IntoMap and @IntoSet annotations respectively.

For a set, return the type you want to put into a set, then you can inject or provide a Set<MyType>.

@Component abstract class MyComponent {
    abstract val allFoos: Set<Foo>

    @IntoSet @Provides protected fun provideFoo1(): Foo = Foo("1")
    @IntoSet @Provdies protected fun provideFoo2(): Foo = Foo("2")
}

For a map, return a Pair<Key, Value>.

@Component abstract class MyComponent {
    abstract val fooMap: Map<String, Foo>
    
    @IntoMap @Provides protected fun provideFoo1(): Pair<String, Foo> = "1" to Foo("1")
    @IntoMap @Provides protected fun provideFoo2(): Pair<String, Foo> = "2" to Foo("2")
}

Function Support

Sometimes you want to delay the creation of a dependency or provide additional params manually. You can do this by injecting a function that returns the dependency instead of the dependency directly.

The simplest case is you take no args, this gives you a function that can create the dep.

@Inject class Foo

@Inject class MyClass(fooCreator: () -> Foo) {
    init {
        val foo = fooCreator()
    }
}

If you define args, you can use these to assist the creation of the dependency. These are passed in as the last arguments to the dependency.

@Inject class Foo(bar: Bar, arg1: String, arg2: String)

@Inject class MyClass(fooCreator: (arg1: String, arg2: String) -> Foo) {
    init {
        val foo = fooCreator("1", "2")
    }
}

Lazy

Similarly, you can inject a Lazy<MyType> to construct and re-use and instance lazily.

@Inject class Foo

@Inject class MyClass(lazyFoo: Lazy<Foo>) {
    val foo by lazyFoo
}

Options

You can provide some additional options to the processor.

  • me.tatarka.inject.enableJavaxAnnotations=true

    @javax.inject.* annotations can be used in in addition to the provided annotations. This can be useful if you are migrating existing code or want to be abstracted from the injection lib you are using on the jvm.

  • me.tatarka.inject.generateCompanionExtensions=true

    This will generate the create() methods on the companion object instead of the component's class. This allows you to do MyComponent.create() instead of MyComponent::class.create(). However, due to a kotlin limitation you will have to explicitly specify a companion object for your component.

    @Component abstract class MyComponent {
      companion object
    }
    
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].