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;
}