I think that most people would agree these days that dependency injection is a good thing for many reasons.
- It allows you to decouple concrete classes from each other
- It makes for a uniform method of object construction
- It simplifies plumbing
- Makes it easier to make testable classes
The problem that I have with it is that, at least in Java, the main frameworks for doing it, being Guice and Spring, can sometimes be as bad as the problem DI tries to solve.
When dealing with DI, the nice thing is that you generally don’t care about where things come from. One of my big problems with DI frameworks has come in the cases where you very much do care. I’ve often had to work around in large code bases where I’m dealing with unfamiliar code and when you’re trying to trace to what thing gets injected, you can be in for a world of pain.
One of the other major pain points is debugging when things go wrong.
It’s one thing when the wrong thing gets injected, it’s another
entirely when something you think should be getting injected isn’t.
nulls have to be treated specially is less than ideal.
Between these and other issues, a tag line at one of my jobs was:
“Guice, turning compile-time errors into run-time errors since at least 2007”
The Problems With Guice
I worked with Guice for years while at Google, and got to spend countless hours debugging injection bugs due to it. Here are a few of my gripes.
First, the stack traces. While they definitely improved over time, the errors you got out could be cryptic.
Second, while most of our Guice usage was at server start up, where speed isn’t a huge issue, it was still surprising how much time it was Guice consuming the CPU. Java reflection isn’t that fast, and especially not fast before the JIT has warmed up.
Third, because Guice creates some classes at runtime using cglib, in some cases, we ran into cases where it unexpectedly blew out permgen. Hopefully in Java 8 land, this shouldn’t be a problem, but if you’re not on Java 8 yet, this is something to consider.
Fourth, locating exactly which binding provided an injected parameter could be a bear. In small projects, this is much less of a problem than in large ones. But when you go searching for bindings, is it a provider method, or bound to an instance, or a class, and depending on which point in the class/interface hierarchy you injected, it can be really difficult to find.
The Problems With Spring
I have quite a bit less experience with Spring (a few months), but what I see, I like even less. These are some of the things I see already:
First, it’s XML (edit: for those who complain that it’s got a java way of binding, if you read the rest of the paragraph you’d see that I knew that), so you lose the use of the Java compiler and type checking. Refactoring can be painful – Eclipse does an OK job, but on class renames, it’s just doing textual substitutions in XML files; what could possibly go wrong? Reputedly there is a Spring UI, but the fact that such a thing even exists points to the fact that it’s a wrong-headed way to do things. Yes there’s a Java way to bind things, but not too many people seem to use it – and it has the same basic problems as Guice.
Second, like Guice, tracking down the bindings is still a pain. Was it bound by class name or or a bean id?
Third, at least in the cases I’ve seen it used, which has been exclusively servlets, it’s too easy to start dumping regular configuration information into the XML, which just offends my sensibilities, but also makes it so now configuration information is now buried in the resulting war file. If only there was a way to not use Spring, that I don’t have to write, that can build configured objects for my servlets that already exists! Uhhh, yeah, there’s context parameters and JNDI resources.
Fourth, it really seems to prefer setter injection, which discourages immutable objects, which is bad. Immutable objects are just a good thing when mutation is really not needed (which is most times), and that requires constructor injection. Yes you can do constructor injection, but it’s kinda ugly.
Lastly, like Guice, injection speed is pretty slow. I was surprised at how long it took for my servlet to initialize considering how much it wasn’t doing.
Manually Writing An Injector
So as an experiment I decided to just manually write an injector myself. I thought first about doing factory classes, but they’re a pain to use, and about as much pain to write. There’s a reason people don’t like them.
So far, with my days of experience with it, I really like it.
- It’s FAST. My servlet now starts in a blink.
- The construction points are easily locatable. I just hit Ctrl-Shift-G in Eclipse on the constructor in question.
- The lifetime of objects is obvious, i.e. do I get a new one each time and which scope does this correspond to?
- Trace-ability! I can easily trace the chain of things leading to construction.
- Missing things fail at compile time!
- I don’t require annotations in the source code, so I can easily inject things that aren’t part of my app.
- I can inject multiple different variants of the same type with all
the annoyance that can be with DI frameworks. No more
@Namedor creating annotations just to distinguish between different values of this or that.
- Because it’s doing injection rather than factory things, it still handles all the nice plumbing bits, so I can add a new request scoped thing at the bottom layer, and not have to plumb any of it to get there.
I can foresee that in a larger app, there could some problem points.
- In multideveloper situations, the injector could be high contention code and you’d have merge conflicts frequently.
- The injector must have java access to anything it makes (public/private/package-private, etc.).
- Wiring up “submodules” could be tricky if they need to talk to each other – providers may be the solution here. Each level would probably coordinate it’s direct descendants.
- You have to write it. But outside of the
singletonfunction, it’s really simple, non-error-prone boilerplate.
How Did I do it?
For non-singleton non-scoped objects, it’s really simple, you just write a function that returns a new thing. For example:
1 2 3 4 5 6 7
For singleton non-scoped objects, it’s a bit more complex, but not too much.
First, you’ll need a reentrant lock. There may be other locking schemes that will work, but this one should work well enough in most cases. I’ll explain later why this is even necessary, and why I chose this and the other ones I thought of and why I didn’t choose them.
You also need a place to stow the singleton object. I use
AtomicReferences to do this. You’ll see why in a second. Anyway,
Then, a method to return the
Configuration (using Java 8 lambdas):
1 2 3
Or, spelled out in non-lambda notation:
1 2 3 4 5 6 7 8
Wait, what’s this
1 2 3 4
Ok, thanks, that was super helpful! Ok, really:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Ok, now for a little rationale. First, the use of
for things is so that only when the thing is empty should anything
ever have to block getting it. Now for the
I first thought I could just synchronize on the
the potential for deadlock is real (where one thread grabs A then B,
and another thread grabs B then A). So that’s why I went with a
global singleton lock.
This limits things to one graph of objects being constructed at once,
so if your root object basically hits all the subobjects, this hurts
you not at all, since your root object is built serially anyway. In
general, the only performance hit you might reasonably take is when
the thing starts up, as once the
AtomicReferences have been filled,
no one locks anymore anyway.
The only potential spot for deadlock is if the providers
spawns a thread that tries to get a global singleton from the injector
and the spawner of that thread waits for that thread to get that
singleton object before proceeding. There’s probably a way to
implement an injector that avoids that mess, but since that situation,
at least in my use case, is highly unlikely, and should be unlikely if
you follow the “constructors only assign members” injection philosophy
(you should), I’m not going to lose any sleep.
What About Request (or other) Scoping?
It’s pretty straightforward really. In this case I have a method on the
injector that returns a new object that contains the
does injection just like above, except with non-static members. So you wind
up with something like this in your injector class:
1 2 3 4
The bits inside the
ReqInjector then look like this:
1 2 3
Or in the case of a per-request singleton, you’d add:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
In my case, I didn’t bother making a separate class, so the per-request injector is actually the same class as the non-scoped one.
What About Circular Injection?
You basically solve it the same way you’d do it in Guice: you inject a provider of the the thing that would cause circularity. For example:
1 2 3
Generally, Java 8 makes these inner provider instances so nice!
A Few Other Things
Having scoped methods in a separate injector class is probably a
better way to do things, as I can see it would be easy to accidentally
have the method accidentally use the static lock, rather than the
instance one which would impact performance more significantly. You
could also accidentally make the
AtomicReference static, which would
end in pain. Fortunately the compiler will prevent you from making
the opposite mistake.
Other than that, if you do things correctly, you should only have to call injector methods at the top level of your app, like in the servlet handlers or the thing that builds your servlet, etc. If you find yourself calling more than one injector method to kick things off, you may be doing something wrong with plumbing, or similar.
While I’ve shown only methods that only just do simple construction, there’s no reason you couldn’t do anything in the methods you write to produce the objects you need.
I’ve not had a need for something like Guice’s
it’s pretty clear how it would work.
One other side effect that I do like is that many of the injector’s
methods can be
private. So just because it’s injected, doesn’t
mean it needs to be exposed.
What about Dagger and Dagger2? Good question. They do appear to solve a number of my beefs with both Spring and Guice, and I’m optimistic if I get there. That said, I’m still not a fan of magic that I’m likely to have to debug through. They may be fine, but until I reach the pain point with the hand-written injector, I’m going to stick with it. I may yet discover some other things with them as time goes on.