The point of this article is to talk about my experience with IoC containers other than the 1000-pound gorilla in the IoC container space.
Through some interesting circumstances (mostly, my hard-headedness about not including 15 megabytes of stuff just to get an IoC container; note that I do know Spring is modular, but it wasn't at the time), I'm one of the few people who hasn't been using Spring. At work, we've mostly used PicoContainer as our middleware "glue" layer. I've also dabbled with Guice, and Plexus (a bit) when messing around with Maven Mojos. This is a summary of my experience with each.
Let's cover the IoC container I feel I know best first--PicoContainer. PicoContainer is a small (the JAR is 100 KB or so), type 3 (constructor injection) IoC library.
And that's pretty much the only thing it will ever be, if you don't customize the library. It does constructor injection. It supports hierarchical containers to resolve dependency conflicts. And that's it.
However, the real power of PicoContainer is its open architecture. You can implement your own component management strategy by implementing
ComponentAdapterFactory. There are a few example adapters. But overall, this lets you futz around with the component instantiation without having to resort to aspect-oriented programming or exotic annotations. Also, since you are in full control of which ComponentAdapter is used for which component, you can completely customize the instantiation on a per-component basis.
As an example of how useful this can be, it was very simple to implement a specialized adapter that can inject some dependencies through construction injection, and then looks for more dependencies to inject using setter injection. When my experiences with Guice left me somewhat lukewarm, I was able to quickly whip up an
@Inject annotation for PicoContainer.
Finally, the hierarchical container facility is extremely useful for one-off dependency injections, such as injecting dependencies in objects retrieved from the persistent store by Hibernate.
So, in conclusion:
- Very small
- Reasonably fast, to the point that it can be used with throwaway containers
- Works even with old JDKs such as 1.4
- Component adapters are extremely flexible
- Containers are very lightweight, can create and throw away child containers at will
- The authors believe constructor injection is the only viable option. However, although constructor injection enforces integrity, it's also very awkward, especially when you get more than 5 dependencies. OK, you probably shouldn't have that many, but sometimes, especially in Struts actions or some other equally centralized point of logic, you will get that many, and it's still less awkward than having a façade just to cut down the dependencies.
- Although the adapters are very flexible, there are very few useful implementations that come with PicoContainer. One that supports Guice-like semantics would be welcome.
- The hierarchical system for resolving dependencies works well in some situations, but is extremely awkward in others. Granted, it's possible to write a component adapter that uses an alternative resolution mechanism, but in the IoC realm, PicoContainer is a bit like assembly language, or Forth; even relatively common cases have to be implemented by hand.
- There is relatively little integration from 3rd-party frameworks (such as Struts, Wicket, and so on). On the other hand, such integration is always very simple to write with PicoContainer, which is not necessarily the case with every IoC container.
- Error stack traces are OK, but sometimes very confusing; specifically, it's not always clear which dependency was missing when trying to instantiate a component.
If you want a small container and don't need many fancy features, or if you prefer to write such features yourself to gain maximum flexibility, PicoContainer fits the deal. In my mind, it's more of an IoC library than an IoC framework.
Guice is a relative newcomer in the IoC container arena. Its main claim of fame, despite what its adopters will tell you, is that it comes from Google. Also, unlike PicoContainer, it came out at a time where people started being annoyed at massive XML files, such as those that show up in many industrial Spring-based code bases.
It immediately grabbed the interest of many a-bloggers, showed up on dzone, yadda yadda. The question is, can you believe the hype?
Well, underneath the hype is actually a very nice IoC container that goes the extra mile to provide you with near-perfect error reporting, and that is not afraid to sacrifice purity to the altar of practicality. Guice supports, out of the box, all sorts of dependency injection scenarios, provided that you mark your dependencies with the
@Inject annotation. From the theoretical point of view, this is more invasive than a run-of-the-mill IoC container, since classic IoC containers should, in theory, let you use plain objects without any special annotations. This, many argue, makes Guice much less flexible than Spring or PicoContainer.
In practice, I found that adding annotations makes sense in most cases. Externalizing all dependency-related informations sounds great in theory, but in practice, you'll end up with 80% of your services having only one sensible implementation. The services with more implementations have different-enough implementations in many cases that the POJO that receives it works well only with one of them (I estimate that about 10% of application services are like this). Guice's use of annotations to mark injectable resources and to let the target object control what resource gets injected to some degree is very practical.
The IoC container is pretty fast, and does some wicked ASM and cglib-fu to provide precise error reporting and the best possible speed regardless of the situation. Overall, it has an "advanced technology" feel to it. It shows a lot of polish in its package structure and in name selection.
However, Guice is a very "closed" system. Being advanced technology is nice, but it does make certain kinds of things a bit more difficult. Here is an example of such difficult things, and incidentally the reason we went with PicoContainer at work. Guice comes in a "uber jar" format; it bundles two versions of ASM (one for cglib, and a more recent version for itself; ASM is noted for major incompatibilities between minor versions, much to the annoyance of projects using it, and cglib appears to be evolving very slowly these days). Hibernate also uses a version of ASM, albeit an older one, as well as the same version of cglib as Guice. So, in an effort to trim the 550 KB or so JAR file to its bare minimum (remember, PicoContainer takes 110 KB...), I tried to get it to work with the older ASM version. The only part that didn't compile was some code used for error reporting, so I figured I could live without it. Well, I was wrong; the previous version of ASM crashes and burns during unit tests. I'm wary of dismissing obscure crashes to situations that will not happen in production code (even though the failing unit test looked like it was doing something exotic), so I left it at that, figuring I was better sticking with PicoContainer and bloating our applications as little as possible.
Some may dismiss this as a trivial reason to not use Guice, and in some ways it is. But it is something to be aware of. I know Hibernate, for one, has had all kinds of problems with use inside environments that come with a different version of ASM. So I'm a bit wary of such annoying dependencies, especially on ASM and cglib which are known troublemakers. Granted, the problem is a bug in ASM, but it does mean that Guice's use of ASM is exotic enough to trigger that bug.
Another problem with such a closed code base is that it's hard to extend it without releasing your own build of it. I'm not that keen with depending on an annotation that's inside the IoC container's package; I'd prefer depending on, say, the
@Resource annotation that's standardized in a JSR (granted, it's not supposed to have exactly the same semantics, but my point is that I dislike putting hard dependencies at the POJO level). I understand the necessity of the annotation, and it's no worse, in my mind, than the
volatile keyword. Except in one way: it binds the code specifically to Guice, not just any IoC container. It would be nice if you could ask Guice to use another annotation type (this is noted as issue 70 in their issue database), but right now, you can't.
- Still reasonably small
- Very advanced, gives a peek of what libraries can look like if people would start to target Java 5 (see also Stripes)
- High-tech, uses all kinds of tricks to maximize performance and give the best error reporting possible
- More popular than PicoContainer at this point, is getting all sorts of frameworks integrated with it
- The only IoC container I know of that allows post-instantiation injection of dependencies for non-constructor injected services. This is actually a great idea in some cases (such as deserialization)
- Used in Struts 2. So it's going to end up being used by somebody visible, unlike PicoContainer :-)
- Sometimes too advanced; that kind of trickery can hurt if you get bit by a bug (admittedly, there doesn't appear to be many of them)
- Binding on a specific in-package annotation is annoying, I'm curious as to why there isn't a way to configure it?
- Not an open architecture the way PicoContainer is. It's very easy to futz around with PicoContainer internals. Guice is much more selective with respect to what it lets you do or not do.
- Java 5 only. Not that I care, but somebody might. Though, they did manage to make it work with Retrotranslator.
If you want a rich IoC container without the huge XML file tradition (yes, Spring folks, I know about JavaConfig, but it's still not what most people use), with sensible defaults and excellent error reporting, Guice fits the bill. However, if you want something with a hood that you can pop open at will, you may prefer another IoC container.
I haven't played with Plexus a lot, so I can't really give too many details. My only experience was inside the Maven 2 codebase.
As far as IoC containers go, it looks OK (although it does suffer from the XML configuration file syndrome, the XML files are lighter than Spring 1.2 configurations [though not Spring 2.0 configurations], and you can use JavaDoc tags to autogenerate the XML file). However, I felt a bit annoyed at it overall. Error reporting, at least within the Maven environment, is not that great, even for simple mistakes like forgetting the appropriate JavaDoc tag or misspelling something. Plus, I can't really figure out where this project fits. It doesn't look like a project that's used by anybody except the Maven guys; why didn't they use PicoContainer to start with instead?
There are a lot of plexus components, but they don't tend to be that reliable, or their use within the Plexus infrastructure is not consistent. A good example is the password input component. Some Maven components use it, some use a plain text input component instead. It's not quite clear, when developing Plexus aware components or Mojos, which you're supposed to use for what.
A pet annoyance of mine: the Plexus guys want you to obtain the logging service through dependency injection. Although that sounds great in theory, I think it's nuts. Logging (at least, programmer logging) is purely a developer service. I'd even prefer not having to create a special object to start logging--if it could figure it out from the current stack with reasonable performance, it would be much better. So, if, to get logging, I need to create a field, a setter, and configure something in an XML file... I'm not going to use logging, or at least, I'll use Jakarta Commons Logging or SLF4J and just forget about Plexus logging.
So, all in all, it looks like an interesting project, but I don't really know why anybody would use it over Spring, or Guice, or even PicoContainer. I guess every developer likes to write an IoC container, because although it's not too hard, there are enough challenges and futzing around with reflection or byte code engineering to make it an interesting task.
So, there you have it, a quick round trip of some lesser-known containers. I also know of a few others:
- HiveMind/Tapestry-ioc, the container behind Tapestry. Well, this one looks powerful, but like Tapestry, it changes wildly between releases, and releases happen relatively frequently. From a pure technology point of view, it looks very powerful, as HLS does not mind looking for good ideas in other projects.
- Yan, a relatively unknown container that appears to allow injecting almost everything in almost anything. The author is one of the few who documented how to use his container in rich domain model. The Spring guys have allowed this only very recently, and it requires the use of the AOP sledgehammer to get it to work.
- And, of course, Spring, whose basic container is becoming more powerful with every release thanks to ideas from all those lesser-known projects, but which remains a huge project. Granted, you can use only parts of it, but it's not the standard usage.
I hope this (longish) post will be of some use to somebody.