Design of a Dependency Injection (DI) Container
Have you ever wondered how a dependency injection container works internally?
Dependency Injection (DI) Containers have become a normal component ,or alternative at least, in modern software architecture and design. Most developers use DI containers as a black box, without knowing how they work internally. Granted, you don't need to know how a DI container works internally in order to use it, but haven't you ever been curious about their implementation?
In this text I will shed some light on how I designed a
fully functional DI container for Java,
Butterfly DI Container, which is faster and more flexible than Spring, Guice and Pico
(yes, I am biased).
Why Not Spring or Pico Container?
You might wonder why I would implement a new DI container when there was already Spring and Pico (Guice wasn't out yet when I started).
Well, I tried out Spring, bought the books etc, and found dependency injection a really nice and powerful idea. But, I actually didn't find Spring itself that elegant. The XML configurations were clumsy to write, and not that easy to read. In addtion, Spring can only inject objects into setters or constructor. You cannot call a method called "add" or "registerJob" or something like that. A totally unnecessary, and very clumsy limitation!
Moreover, Spring seemed to be focusing on a lot of peripheral components (AOP, MVC, JDBC Templates etc.) I rarely needed, thereby somewhat neglecting the core product - the DI container. And then there were all the external dependencies that Spring shipped with, without explanation of which were needed when. I could go on about the small, rough edges in Spring that made me start this project, but I think you get the point:
I wanted a pure DI container with ZERO external dependencies (Spring's core actually doesn't have any, but it doesn't say anywhere), and with a lot more core functionality (DI features) than I found in Spring.
Then I looked to Pico, but the whole concept of programmatic configuration seemed too verbose compared to how dense it could be with a tailored domain specific language (DSL) for DI. In addition, Pico container seems to require a tree/graph of containers in order to achieve what you want with singletons, new instances etc. I wanted a SINGLE container with support for both programmatic and scripted configuration (both have their merits in different situations).
So, having decided to implement my own dependency injection container I wrote down some core design goals:
- The container itself should be independent of its configuration mechanism.
- A simple configuration language - simpler than Java.
- A flexible internal design making it easy to link factories
to each other, and to add new factory types later. It should also support
different methods of configuration.
- Zero external dependencies.
In this article series I will go through some of the design decisions I made during the design of both the script language, and the internal design of the dependency injection container.
Note: This article series is still work in progress. More texts will be added over time. To be notified of new texts in this trail, subscribe to my RSS feed.
Here is a list of the texts published so far in this article series about DI container design: