about:drewcsillag

Musings of a programmer, musician, photographer, and Christian.

Dependency Injection Without a Framework

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. That 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 @Named or 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 singleton function, 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
public static Thing getThing() {
  return new Thing();
}

public static OtherThing getOtherThing() {
  return new OtherThing("fixedValue", getThing());
}

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.

1
2
private static final ReentrantLock GLOBAL_SINGLETON_LOCK
    = new ReentrantLock();

You also need a place to stow the singleton object. I use AtomicReferences to do this. You’ll see why in a second. Anyway, for example:

1
2
private static AtomicReference<Configuration> configuration
    = new AtomicReference<Configuration>();

Then, a method to return the Configuration (using Java 8 lambdas):

1
2
3
private static Configuration getConfig() {
  return singleton(configuration, () -> new Configuration());
}

Or, spelled out in non-lambda notation:

1
2
3
4
5
6
7
8
private static Configuration getConfig() {
  return singleton(configuration, new Provider<Configuration>() {
    @Override
    public Configuration get() {
      return new Configuration();
    }
  });
}

Wait, what’s this singleton method?

1
2
3
4
private static <T> T singleton(final AtomicReference<T> reference,
    final Provider<T> provider) {
  return singleton(reference, provider, GLOBAL_SINGLETON_LOCK);
}

Ok, thanks, that was super helpful! Ok, really:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private static <T> T singleton(final AtomicReference<T> reference,
    final Provider<T> provider, final ReentrantLock lock) {
  if (reference.get() != null) {
    return reference.get();
  }
  lock.lock();
  try {
    if (reference.get() == null) {
      final T item = provider.get();
      reference.set(item);
    }
    return reference.get();
  } finally {
    lock.unlock();
  }
}

Ok, now for a little rationale. First, the use of AtomicReferences for things is so that only when the thing is empty should anything ever have to block getting it. Now for the GLOBAL_SINGLETON_LOCK. I first thought I could just synchronize on the AtomicReference, but 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 get method 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 HttpServletRequest and 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
public static ReqInjector perRequest(
    final HttpServletRequest servletRequest) {
  return new ReqInjector(servletRequest);
}

The bits inside the ReqInjector then look like this:

1
2
3
public X509Certificate[] getCerts() {
  return (X509Certificate[]) servletRequest.getAttribute(X509);
}

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
private final ReentrantLock perRequestLock = new ReentrantLock();

private AtomicReference<RequestScopedThing> requestScopedThing
    = new AtomicReference<RequestScopedThing>();

private <T> T scopedSingleton(final AtomicReference<T> reference,
    final Provider<T> maker) {
  return singleton(reference, maker, perRequestLock);
}

public RequestScopedThing getRequestScopedThing() {
  return scopedSingleton(requestScopedThing,
      () -> return new RequestScopedThing(getThing(), getOtherThing()))
}

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
public Circular getCircular() {
  return new Circular(getThing(), () -> getThingThatWouldCauseACycle());
}

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 @AssistedInject but 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.

Comments