Inversion of control vs. dependency injection

The key difference between inversion of control and dependency injection is that inversion of control requires the use of an external framework to manage resources, while dependency injection provides a way to access those resources without directly referencing the framework that manages them.

IoC is an incredibly simple design principle. It asserts that developers should keep their business logic clean by using a specialized, external framework to take care of tasks and functions such as the following:

  • The configuration of highly parameterized components.
  • The management of objects with complicated lifecycle requirements.
  • The handling of low-level systems interactions.

Every popular software development framework, from Angular to Flask, implements the inversion of control principle by providing an IoC container that manages and provisions components. In fact, implementing IoC is the defining feature of a framework. If a framework doesn't do IoC, then it's a library, not a framework.

IoC frameworks are largely hands-off. They are said to implement the Hollywood principle -- "don't call us, we'll call you." That's how frameworks are supposed to work.

With an IoC framework, a developer writes business logic. When the business logic needs to run, the framework invokes it. Optimally, an IoC framework works its magic behind the scenes and is never directly referenced in the code.

However, there are times when a developer needs to access a component that is managed by the container. Put another way, the developer's code has a dependency on a component managed by the framework.

For example, in a set of Spring tutorials I've recently posted, I have a Game class that depends on a container managed Score object to keep track of wins, losses and ties.

One way for the Game to gain access to the container managed Score is by pulling it directly out of the Spring framework's container. The following is the code to do so.

Score score = spring.getBean(Score.class);

Do you see the problem here? Because it's a big one.

We initially said that the goal of inversion of control is to keep code simple and business logic clean. Yet we now have a direct reference inside our business logic to the Spring framework. In our effort to remove a few lines of peripheral code from our business logic, we added a direct reference to a framework that's comprised of thousands of lines of code that we don't maintain. We've swallowed a spider to catch a fly.

This is where dependency injection comes in. This feature allows references to components that are managed by an IoC container to be automatically provisioned without the need to directly reference the container in your code.

The following is an example of dependency injection at work. Rather than hardcode a reference to the Spring framework, the Autowired annotation asks the Spring framework to inject an instance of the Score class at the point where the Game class has a dependency on it.

@Component
class Score {
 int wins, losses, ties;
}

@Component
class Game {

 @Autowired
 Score score;

 public void playTheGame(String gesture) {
  //Score score = spring.getBean(Score.class);
  score.wins++;
 }
}

In this example, the IoC container manages the lifecycles of both the Game and the Score classes.

When the Game class needs an instance of the Score, the IoC container knows to automatically provide one due to the placement of the @Autowired annotation.

This is dependency in action. The Game class has a dependency on the Score class, and the IoC container manages the Score. When the Score class is needed by the Game, the IoC container injects one in -- thus the term dependency injection.

What is most significant about this example is that there is no direct reference in the code to the Spring framework. That's the whole point of dependency injection, and that's how dependency injection is tied to the concept of inversion of control. Dependency injection enables inversion of control without tightly coupling business logic to the inversion of control container.

Cameron McKenzie has been a Java EE software engineer for 20 years. His current specialties include Agile development; DevOps; Spring; and container-based technologies such as Docker, Swarm and Kubernetes.

View All Videos