Inversion of control (IoC) is a mechanism used to externalise the creation and management of component dependencies.
What a mouthful! What does that mean? Isn’t there a simpler explanation? How does this relate to dependency injection?
The concept is actually very simple: IoC gives a component the objects it needs to do its job.
Any non-trivial application is made up of many classes that work with each other to execute the required business logic. Usually each object is responsible for creating its own references to the objects it works with. This leads to tightly coupled code. Tightly coupled code is difficult to modify, test and reuse.
However, without any code coupling, no meaningful work can actually be done. In order to do something useful, classes need to know about each other. A certain amount of coupling is necessary, but this must be carefully controlled.
Who Controls The Dependencies?
Normally we will manually create and control all the connections (dependencies) between various parts of the code. However, when we use an inversion of control container, we ask the container to create and control those interconnections. This architecture inverts control as compared to traditional programming practices.
Using a IoC container, objects are given their dependencies at creation time. The container controls each object in the application. Objects don’t create or obtain their own dependencies. Instead the container creates and controls these dependencies. This is referred to as the container injecting dependencies, hence a dependency injection (DI) container. This is often also called wiring.
One of the benefits of using an IoC container is that we can focus on the business logic. We don’t have to spend a lot of time on the “where and how” of creating and/or referring to a correctly initialised component with all of its dependencies. Dependency injection allows the container to create those references for us, without us having to manually manage all the dependencies.
The terms dependency injection and inversion of control are generally used interchangeably, although dependency injection is just one of a number of ways to implement IoC.
A Dependency Injection Example
As a concrete example, let’s think about an InvoiceProcessor
class that depends on an instance of a DatabaseManager
class to load and store the processed invoices.
Using the traditional programming style, the InvoiceProcessor
would create an instance of a DatabaseManager
either by using the new
operator or by getting an instance from a factory class. The InvoiceProcessor
would need to know how to create a DatabaseManager
instance, what parameters to pass to it, check for null
s, handle exceptions, etc.
public class InvoiceProcessor { // dependency private DatabaseManager dbManager; // constructor public InvoiceProcessor () { // create a DatabaseManager object here dbManager = ...; } // other code ... }
Using the IoC approach, the DI container creates an instance of DatabaseManager
(or one of its subclasses), and then passes this instance to the InvoiceProcessor
at runtime. The InvoiceProcessor
doesn’t know or care how the DatabaseManager
was created; all it knows is that it has a properly initialised DatabaseManager
object to work with.
public class InvoiceProcessor { // This is one way to specify dependency injection. // Container injects a DatabaseManager instance here. @Inject private DatabaseManager dbManager; // other code using the dbManager }
Dependency Inversion Principle
The only major restriction we have is to write classes in a way that supports the DI container. We must conform to the Dependency Inversion principle. This is the principle of coding to an interface, not an implementation. An object should only know about its dependencies through their interfaces, and not through their physical implementations. This means that the container can inject any concrete class that implements that specific interface. Different concrete classes can be swapped out without the dependent object knowing the difference.
Dependency Injection Containers
I’ve been talking about DI containers throughout, but what are they? There are many DI containers for Java. The most popular are the Spring framework and the JEE (Java/Jakarta Enterprise Edition).
JEE was one of the first containers for Java enterprise applications. The first versions of J2EE were heavyweight IoC containers. They only offered dependency lookup, which is harder to use. The later versions of JEE use dependency injection, and have become easier to use.
Spring was one of the first DI frameworks available, and it offered a lighter-weight alternative to J2EE. Spring has grown exponentially. It is now one of the most popular and powerful frameworks. Spring is a more like a framework of frameworks, and provides elegant solutions for just about anything you ever want to do in Java.
Google Guice (pronounced “juice”) is another lightweight DI framework and is largely limited to DI only. Other smaller DI containers include PicoContainer and Dagger (a compile-time DI framework, mainly for Android apps).
What Next?
This introduced the concept of dependency injection. Next week I’ll look at dependency injection in more detail. So stay tuned!
I enjoy your views and comments, so please share them. And if you want to learn about topics like this, subscribe to my weekly Java tips.