Changes & Migration from Kodein 4 to Kodein DI 5
This guide will help you migrate from Kodein 4 to Kodein 5+.
-
The first part explains the changes and their reasoning.
-
The second part gives steps to follow to port your code to Kodein 5+.
Changes
New repository: JCenter
Kodein has moved from MavenCentral to JCenter.
The main reason for this move is that I need download statistics for professional reasons.
Also, JCenter is a CDN.
Also, JCenter allows me to manage a dedicated repo for beta releases.
Kodein is an open-source product and will always remain so. There will never be a "pro" paid version! The statistics are just to give me an "argument" in my future projects ;) |
To you, it means that:
-
You need to add the JCenter repository to your build system (Gradle or Maven).
-
If you are using a beta version of Kodein, you need to add the Kodein specific repository.
-
That’s all ;)
New package: org.kodein.di
First and foremost, Kodein now has its dedicated domain name: kodein.org.
Kodein is now located inside the package org.kodein.di
.
laziness
The biggest change in Kodein 5 is that everything in Kodein 5 is lazy.
This is because:
-
You shouldn’t know whether the instance you are asking for will be easy to reatrieve or hard to construct.
-
Some classes (such as Android or TornadoFX components) are created before being "attached" to their context.
Furthermore, the use of Kotlin’s delegated properties allows:
-
Factories to access context & receiver (very useful for TornadoFX & Android integration).
-
The use of a trigger (previously known as injector) a lot easier.
The use of delegated properties does not significantly impact performance! |
However, if you don’t want all those benefits and still want direct access, Kodein 5 provides DKodein
and DKodeinAware
(D is for Direct), which works exactly like Kodein 4’s Kodein
and KodeinAware
.
Currying
In Kodein 4, you would curry a factory into an instance by using kodein.with(argument).instance()
, which is not really readable / understandable.
In Kodein 5, you curry via the arg
parameter: kodein.instance(arg = argument)
.
Injector
Kodein 5 radically changes the way the "injector" works.
In version 4, you would create a KodeinInjector
and use it to retrieve dependencies.
In version 5, Kodein offers the KodeinTrigger
and the LateinitKodein
that each take a role of the previous KodeinInjector
.
-
KodeinTrigger
allows you to define when you want your dependencies to be retrieved (rather than at first access) -
LateInitKodein
allows you to use a Kodein-like shallow object and to define the location of a real Kodein object later.
In most cases, you won’t need either of these classes, even if you were using a KodeinInjector .
Because Kodein 5 is lazy by default, it eliminates most of the injector needs.
|
Scope & contexts
In Kodein 4, scopes used the factory argument as scope context.
In Kodein 5, there are a few new variable that Kodein uses to find binding & call factories.
Scopes now use a context
value, which means that there can now be some scoped multiton (the scope uses the context while the multiton uses the argument).
Kodein 5 also allows the binding factories to access the receiver of the property. This is very useful for framework integration such as Android & TornadoFX. |
The Singleton
binding is now supercharged.
It handles scopes AND references, which means that it is now possible for weird stuff such as "weaked scoped singleton".
The multiton binding is the same!
|
Finally, there are no more such things as "auto-scopes".
They are replaced by scopes that do not need a particular provided context, e.g. Scope<Any?, *>
.
Migration
New repository: JCenter
You need to add the jcenter repository to your project (if it is not already).
New modules
Module names have changed:
Old name | New name |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
New package: org.kodein.di
Well, the new package is now org.kodein.di
. So, you know, update the imports ;)
Old name | New name |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
The com.github.salomonbrys.kodein
package has been split up between core and generic code.
This allows Kodein to be used with Java 9.
Laziness
This is by far the most important change in your migration.
For JVM / Server
You now have a choice : either you embrace this new laziness philosophy, or you fight it ;) In fact, both choice are pretty easy to make!
Injection, a.k.a. kodein.newInstance { } , is not impacted.
Only retrieval is.
|
Lazy by default
If you want to use laziness by default, as Kodein now does, simply replace all =
with by
.
// Kodein 4
val ds: DataSource = kodein.instance()
// Kodein 5
val ds: DataSource by kodein.instance()
However, if you don’t want to use laziness, you need to update your code to
Direct by default
If you want to keep Kodein 4’s direct retrieval by default, you simply need to:
-
handle
DKodein
objects instead ofKodein
-
have your classes being
DKodeinAware
instead ofKodeinAware
DKodein
val kodein = Kodein.direct { (1)
/* bindings */
}
1 | This .direct will return a DKodein |
DKodeinAware
classclass MyController(override val dkodein: DKodein) : DKodeinAware
For Android
In Android, the laziness by default is actually a very good thing, as you don’t need things like KodeinInjector
or LazyKodein
:
Also, appKodein()
is replaced by closestKodein()
KodeinAware
Activityclass MyActivity() : Activity(), KodeinAware {
override val kodein by closestKodein()
private val usersManagers: UsersManagers by instance()
}
If you were using Android specific types (such as KodeinActivity ), you should read the Android section.
|
Currying
There’s a new currying syntax:
// Kodein 4
val dice: Dice = kodein.with(6).instance()
// Kodein 5
val ds: DataSource by kodein.instance(arg = 6)
Much like the tag argument, the arg argument should always be named.
|
Injector
If you were using a KodeinInjector
, you now need to use a KodeinTrigger
and/or a LateInitKodein
.
Trigger
The KodeinTrigger
class allows you to define when the dependencies will be retrieved (as opposed to lazily when needed).
By itself
You can use the on
function to create a Kodein
object that is bound to a KodeinTrigger
:
val tk = kodein.on(trigger = KodeinTrigger())
val ds: DataSource by tk.instance()
tk.kodeinTrigger!!.trigger()
In KodeinAware
It is really easy to use a KodeinTrigger
in a KodeinAware class:
class MyManager(override val kodein: Kodein) : KodeinAware {
override val kodeinTrigger = KodeinTrigger() (1)
val ds: DataSource by tk.instance()
init {
kodeinTrigger.trigger() (2)
}
}
1 | The kodeinTrigger property of a KodeinAware class |
Late init
In KodeinAware
In a KodeinAware
class, you can set the kodein
value to be late init
:
class MyManager : KodeinAware {
override late init var kodein: Kodein (1)
val ds: DataSource by instance()
init {
kodein = applicationGlobals.kodein (2)
}
}
1 | The kodein property is late init , which is not a problem as long as you set it before accessing a dependency. |
2 | You can access dependencies after setting the kodein variable. |
Scope contexts
Scopes now use the context
variable, instead of the factory argument arg
.
Binding with a scope
The syntax to bind a scoped singleton has evolved:
val kodein = Kodein {
// Kodein 4
bind<Session>() with scopedSingleton(requestScope) { /* ... */ }
// Kodein 5
bind<Session>() with scoped(requestScope).singleton { /* ... */ }
}
Getting a scoped singleton instance
Retrieval context
A scope is now defined function of a context
, rather than of an arg
:
val kodein = Kodein {
// Kodein 4
val session by kodein.with(request).instance()
// Kodein 5
val session by kodein.on(context = request).instance()
}
KodeinAware context
You can define a context that works for an entire KodeinAware
class.
class MyController(request: Request) : KodeinAware {
override val kodeinContext = kcontext(request)
val session by instance()
}
Setting a kodeinContext still allows you to acces bindings without scopes!
The kodeinContext is the context by default, but will not be used if there is no need for a context.
|
Android
The Kodein Android extension has radically changed. It is therefore advised to read the new Kodein on Android documentation.
Laziness
Because everything is lazy be default, it is now very easy to use KodeinAware
with Android components (note that closestKodein
replaces appKodein
):
class MyActivity : Activity(), KodeinAware {
override val kodein by closestKodein() (1)
val ds: DataSource by instance()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ds.connect() (2)
/* ... */
}
}
1 | The closestKodein function replaces the appKodein v4 function. |
2 | Because everything is lazy, the kodein AND ds instances will both be retrieved only when needed, which is at that time. |
Layered dependencies
The KodeinActivity
class, KodeinInjector
interface, and all their component equivalent are gone.
what they did was:
-
Provide an injector, which is no longer needed since Kodein 5 is lazy by default.
-
Allowed to create a "sub-kodein" with some new or overriding bindings, which this guide explains how to do.
-
Provide a Kodein object that reflects those new or overriding bindings, which is what
closestKodein
does.
To create a component that creates it’s own Kodein object, simply override the kodein
value, create a new Kodein object, and extend the parent kodein in it:
class MyActivity : Activity(), KodeinAware {
private val _parentKodein by closestKodein() (1)
override val kodein: Kodein by Kodein {
extend(_parentKodein) (2)
/* activity specific bindings */
}
}
1 | Get the "global" application kodein. |
2 | Extends the "global" application kodein, to be able to access, with this new Kodein object, all bindings defined at the application level. |
Scopes
All android scopes are replaced by the androidScope
function:
val kodein = Kodein {
// Kodein 4
bind<Whatever>() with scopedSingleton(activityScope) { /* ... */ }
// Kodein 5
bind<Whatever>() with scoped(androidScope<Activity>()).singleton { /* ... */ }
}
Modules
The androidModule
still exists but now need the application context:
class MyApplication : Application(), KodeinAware {
override val kodein = Kodein.lazy {
import(androidModule(this@MyApplication))
/* bindings */
}
}
The Example: defining the android context as the kodein context
|