https://kotlinlang.org logo
Title
n

nwh

11/13/2018, 2:09 AM
For standalone (non-Android) applications, what is everyone's preferred DI library?
g

gildor

11/13/2018, 2:23 AM
I believe Dagger2 is still the best option if you need fast and most scalable solution
c

Casey Brooks

11/13/2018, 2:47 AM
I prefer Guice, been using it for years. Less boilerplate and much easier to set up, and being a reflection-based lib makes it possible to use it for a simple plugin system. Only downside is that it has long startup times, but for long-running applications this is easy to ignore.
g

gildor

11/13/2018, 2:48 AM
What kind boilerplate do you have in Dagger?
You also can use Dagger for plugins using multi binding
But I agree, reflection based can have some advantages, but still on scale annotation processing can give significant advantages about speed and compile-time checks
c

Casey Brooks

11/13/2018, 2:51 AM
For boilerplate, Guice has no need for Components or anything like it, and the modules are a bit cleaner as well.
For plugins, you need to recompile the core in order to support a new module, which wouldn’t work in a framework setting. You can take your framework, a bunch of modules compiled elsewhere, and still get them to build a single graph entirely at runtime (perhaps through configuration files or classpath scanning to find these modules)
But I totally agree, the nature of reflection DI does mean you lose some performance and safety, but it is by no means a deal breaker IMO
g

gildor

11/13/2018, 2:55 AM
But if you have only one scope for whole application and only constructor injections, you component will have one method. I agree, this is can be considered as boilerplate, but also it’s just more explicit
It depends of course, I agree
s

Shawn

11/13/2018, 4:36 AM
fwiw I don’t use anything but constructor injection, field injection is scary stuff at times lol
d

David Hamilton

11/13/2018, 8:03 AM
So... given the power of Kotlin's
lazy
, what are the advantages of a DI library over using lazy `val`s (singletons) and `fun`s (providers) in a custom Dependencies class?
g

gildor

11/13/2018, 8:08 AM
Because DI has nothing related to lazy
And DI is not about singletons
lazy property for dependency creation is actually against DI rules. You should provide dependencies from outside, do not create them on the same class
Of course you can write Service Locator using delegate properties to request required dependencies, but again, this is dependency inversion but not DI
and has the main problem of service locator: class has hard dependency on service locator implementation
d

David Hamilton

11/13/2018, 8:11 AM
Ahhh - I wasn't clear
I mean create a Dependencies class with all the injectable resources as vals and inject one into the other into the lazy initialisers. They're external and injectable
g

gildor

11/13/2018, 8:12 AM
so every class that need any dependency now has hard dependency to Dependencies
which is also not really good solution, especially on big scale
it’s even worse that service locator
d

David Hamilton

11/13/2018, 8:13 AM
Other way around
g

gildor

11/13/2018, 8:14 AM
DI is:
class SomeClass(val dep1: SomeDep1, val dep2: SomeDep2)
or the same with method injection (which has a lot of problems, especially in Kotlin with nullability)
So you have inversion of control and class doesn’t have any hard dependencies to any Injector, ServiceLocator and so on
Other way around
could you show an example
d

David Hamilton

11/13/2018, 8:16 AM
open class Dependencies() {
    open val serviceB: ServiceB by lazy {
        ServiceB( serviceA)
    }
    open val serviceA: ServiceA by lazy {
        ServiceA()
    }
g

gildor

11/13/2018, 8:16 AM
Okay, where is dependency injection here?
d

David Hamilton

11/13/2018, 8:17 AM
Sorry, more haste, less speed 😄
Obvs ServiceA instance is lazily created when it needs to be injected into B, (or is used elsewhere)
Class can easily be overridden by subclasses to inject test dependencies
g

gildor

11/13/2018, 8:23 AM
This is manual dependency injection, yes
now add 10 more dependencies
and some graph of them
d

David Hamilton

11/13/2018, 8:24 AM
Sure, but if I compare with my Guice definitions, it's hardly, if any, more complicated
g

gildor

11/13/2018, 8:24 AM
But I agree, Kotlin simplifies manual dependency injection code a lot
yes, sorry, just now get your point, yes, this may be a valid approach of course, but mostly on relatively small scale
we use similar approach on one of our small projects
but looks that when this graph will grow it will be really hard to support it
d

David Hamilton

11/13/2018, 8:25 AM
Yes, true
g

gildor

11/13/2018, 8:27 AM
Sure, but if I compare with my Guice definitions, it’s hardly, if any, more complicated
If you use constructor with
@Inject
annotation and your modules mostly just maps interfaces to implementations (or even don’t do this) than Guice or Dagger will save a lot of boilerplate for you
d

David Hamilton

11/13/2018, 8:28 AM
🤔 Maybe I missed a trick with Guice! Checking...
g

gildor

11/13/2018, 8:29 AM
Because main boilerplate in this case will be defining new dependencies in your Dependencies class, invoking constructors and passing dependencies, which completely eliminated by Guice reflections or Dagger code generations
Of course DI framework adds additional configuration overhead and on small scale this overhead can be higher than saved boilerplate
but if you have relatively big graph you save a lot of code, especially if you use mostly constructor injections with dependencies that exist on the same graph (which is should be you preferred way to work with dependencies)
d

David Hamilton

11/13/2018, 8:34 AM
Ah, yes. I used the
@Provides
annotation in Guice, which yields a Module almost identical to that in the lazy example
g

gildor

11/13/2018, 8:37 AM
Yes,
@Provides
significantly increase amount of code, but in most cases you actually can avoid this
even for complicated cases when you need dynamic parameter as dependency, it’s possible to use
@AssistedInject
for this
d

David Hamilton

11/13/2018, 9:17 AM
IIRC, my main driver in using
@Provides
was to allow test contexts to override certain dependencies. Is that possible using
@Inject
?
g

gildor

11/13/2018, 9:22 AM
But you can override test dependencies, but you need module for that
Because you can override @Inject dependency with @Provides dependency
Do you need this for some integration tests?
👍 1
Also I’m not so familiar with Guice, but this is reflection, so you probably can just replace it on reflection level without actual updating of graph