JSR-330: Using reflexivity to auto-inject
Introduction
Kodein offers a module that implements reflexivity injection based on the JSR-330 javax.inject.*
annotations.
There are two reasons to use this module:
-
You are moving a code base from a Java injection library (such as Guice or Dagger) and want the Java code to work the same while there still is injected java code.
-
You want to easily use Kodein in a Java code.
-
That’s it!
Using this module with Kotlin code means adding a lot of reflexivity at run-time that can easily be avoided in Kotlin (but not in Java).
Every-thing that is described here is a lot less performant than using classic DI injection methods. PLEASE DO NOT USE ON KOTLIN CLASSES. Kittens will die painfully if you do! |
Install
JavaX injections
Constructor injection
You can create a new instance of a given class, provided that:
-
The class has only one constructor
-
Or the class have one of its constructors annotated with
@Inject
.
It is a good practice, however, to always have an @Inject
constructor, even if it is the only constructor.
public class MyJavaController {
@Inject
public MyJavaController(Connection connection, FileSystem fs) {
/* ... */
}
/* ... */
}
You can then create instances of such classes by using di.jx
in Kotlin, or Jx.of(di)
in Java.
val controller = di.jx.newInstance<MyJavaController>()
MyJavaController controller = Jx.of(di).newInstance(MyJavaController.class);
Field injection
You can inject fields of a class by annotating them with @Inject
.
public class MyJavaController {
@Inject
Connection connection;
@Inject
FileSystem fs;
/* ... */
}
You can then inject existing instances of such classes by using di.jx
in Kotlin, or Jx.of(di)
in Java.
val controller = MyJavaController()
di.jx.inject(controller)
MyJavaController controller = new MyJavaController();
Jx.of(di).inject(controller);
Method injection
Method injection is supported to be compatible with Java injection libraries. It is, however, widely considered as the less semantic injection method. |
You can have @Inject
annotated method be called at injection.
public class MyJavaController {
@Inject
public setIO(Connection connection, FileSystem fs) {
/* ... */
}
/* ... */
}
You know the drill, use di.jx
in Kotlin or Jx.of(di)
in Java the exact same way as for field injection.
Being specific
Qualifiers annotations
javax.inject
libraries use the concept of "qualifier annotations", which serves the same purpose as DI’s tag system.
The @Named
annotation is a qualifier provided by default, and is supported by default in Kodein-JxInject.
In Java, any field or method / constructor parameter annotated with @Named("whatever")
will use the String
value as tag.
public class MyJavaController {
@Inject @Named("SQL")
Connection connection; (1)
@Inject setConnection(@Named("SQL") Connection connection) { /*...*/ } (2)
}
<1>: Field injection. <2>: Method injection.
To inject the connection
field, DI will essentially retrieve as di.instance<Connection>(tag = "SQL")
.
For any other qualifier annotation, you need to provide a function that will transform a qualifier annotation to a tag.
val di = DI {
import(jxInjectorModule)
/* Other bindings */
jxQualifier<MyQualifier> { MyTag(it.value) } (1)
}
<1>: Transforms a MyQualifier
qualifier annotation into a MyTag
DI tag.
Using erased bindings
If you need to inject erased binding, you can annotate the field or method / constructor parameter with the @ErasedBinding
annotation.
public class MyJavaController {
@Inject @ErasedBinding List<Connection> connections;
}
Optional injection
If you need to inject something only if it was bound (and set it to null otherwise), you can use the @OrNull
annotation.
public class MyJavaController {
@Inject @OrNull Connection connectionOrNull;
}
Provider & Factory injection
You can inject a provider, either by using javax.inject.Provider
or kotlin.jvm.functions.Function0
.
Note that if you are using the latter, you need to use the @ProviderFun
annotation.
public class MyJavaController {
@Inject Provider<Connection> connectionJXProvider;
@Inject @ProviderFun Function0<Connection> connectionKotlinProvider;
}
To inject a factory, you need to use kotlin.jvm.functions.Function1
annotated with @FactoryFun
.
public class MyJavaController {
@Inject @ProviderFun Function1<String, Connection> connectionFactory;
}