How to run Dagger 2 code in a CDI container
Posted by mkouba in Martin Kouba's Blog on Jan 27, 2016 5:05:22 AMDagger 2 is a popular dependency injection solution for Android and Java. It's designed primarily for restricted environments with limited resources. And it's also important that it's built on javax.inject
annotations (JSR 330). If we dive into Dagger 2 internals we find out that all the important stuff is done at compile time. Annotation processors are responsible for lookup of bindings, validation of injection points, and finally generating the source code of factories responsible for instance creation. This approach brings advantages (for instance easier debugging and tiny runtime) and also limitations (no dynamism at runtime, injected fields cannot be private as no reflection is used, etc.).
Now the question is whether it would be possible to run Dagger 2 code in a CDI container? Sounds like a silly question, right? It should be feasible from the technical point of view but why would we need this? Well, imagine a useful library written in Dagger 2 which we would like to reuse in a CDI application. Or theoretically, we could start with Dagger 2 and when the application grows we need more advanced features (events, interceptors, etc.). We could either rewrite the code or emulate Dagger 2 behaviour and don't touch the code at all.
So how do we emulate Dagger 2 in a CDI container? There is a simple emulator prototype prepared. Actually, there are only two classes. An extension which turns @Provides
methods into producer methods and also restricts the set of bean types of each "emulated" bean (in Dagger 2 a binding, represented by a @Provides
method or a class with an injectable constructor, can only have one type and one qualifier). And LazyAdaptor
bean which is used for all injection points with dagger.Lazy (lazily-computed values) as a requested type. That's all. Injection points should not require any modifications. @Singleton
scope is also supported in CDI. Note that unless a normal scope or interceptors are used we work with ordinary instances, i.e. no internal container constructs (e.g. client proxies) are used. Of course, this prototype is definitely not feature complete but it should work for simple use cases.
Checkout the repository https://github.com/mkouba/dagger2cdi and run mvn clean test
. You should see the same output as for the Dagger's coffee example. The test is using the same test classes (CoffeeMaker, Thermosiphon, ...). Of course, the bootstrap code is different (we're using Weld SE).
Conclusion? It seems that it's quite straightforward to include "common" Dagger 2 code into an existing CDI application.
Comments