Skip to content
← blog/

Understanding Inversion of Control and Dependency Injection

5 min read #springboot #java #software-engineering #spring

Inversion of Control and Dependency Injection are two important concepts in the Spring Framework. They are closely related and work together to achieve loose coupling and maintainability in software applications.

Inversion of Control (IOC)

Inversion of Control is an architectural principle that tells how components interact within an application. Traditionally a class or component is responsible for creating and managing its dependencies.

class Bike{
      public Honda honda= new Honda();
  }
//or
class Bike{
      private Honda honda;
      public Bike(){
             honda=new Honda();
       }
}

In the above code approach class Bike is responsible for creating and managing Honda Object. The provided code snippet, the Bike class has a tight coupling with the Honda class

Issues with the above approach

  1. Low Flexibility: The Bike class directly creates an instance of the Honda class using the new keyword. This creates a strong dependency between Bike and Honda, making it difficult to switch to a different motorcycle brand without modifying the Bike class
  2. Testing is tough: Since the Bike class creates an instance of Honda internally, it’s hard to replace the Honda instance with a mock object for testing purposes. This makes unit testing challenging, as you can’t easily isolate the behavior of the Bike class from the Honda class.
  3. Scalability Problem: If you want to introduce more features or variations of bikes (e.g., different brands or models), you’ll need to modify the Bike class, violating the Open/Closed Principle of SOLID design.

IoC promotes the idea that a class should not directly create its dependencies but should receive them from an external source. In Spring The IoC container takes responsibility for managing the lifecycle of objects, creating and wiring them together based on the configuration provided by the developer. It decouples the components from their dependencies, promoting loose coupling and making the code more flexible and maintainable.

We will learn more about spring containers or IOC containers in depth later in a different blog.

Here’s how you could improve the code using IoC principles:

class Bike {
    private Motorcycle motorcycle;

    public Bike(Motorcycle motorcycle) {
        this.motorcycle = motorcycle;
    }
}

Now, the Bike class expects a Motorcycle instance to be injected during its construction. This way, the responsibility of creating and providing the Motorcycle instance is shifted outside of the Bike class, adhering to the IoC principle.

Advantages:

  1. High Flexibility: Above code is highly flexible because there is loose coupling between Bike and Motorcycle. The Bike class is no longer tightly coupled to a specific motorcycle brand (e.g., Honda). You can inject any implementation of the Motorcycle interface, allowing for easy swapping or extension without modifying the Bike class
  2. Testing: During unit testing, we can provide a mock implementation of Motorcycle. to Bike class to isolate the testing of the Bike class’s behavior
  3. Scalability: The Bike class is now open for extension (e.g., adding new brands) without modifying its existing code.

Dependency Injection(DI)

DI is a specific technique that implements IoC by managing the injection of dependencies into components. Dependency Injection is a specific implementation of IoC. It is a design pattern that allows the injection of dependencies into a class rather than the class creating or obtaining them directly.

DI promotes loose coupling between classes, as the dependencies can be easily replaced or changed without modifying the dependent class.

In the context of Spring, DI means that the Spring IoC container is responsible for resolving and injecting the dependencies into beans when they are created. This is typically done through constructor injection or setter injection.

There are three types of DI in Spring:

  • Constructor Injection: Constructor is used to inject the dependency in component
  • Setter Injection: The dependencies are set through setter methods in the dependent class.
  • Field Injection: The dependencies are directly injected into the dependent class’s fields using annotations.
public interface Motercycle {
    // Motercycle interface methods
}

public class Honda implements Motercycle {
    // Honda implementation of Motercycle interface
}

public class Bike {
    private Motercycle motercycle;

    // Setter for Motercycle
    public void setMotercycle(Motercycle motercycle) {
        this.motercycle = motercycle;
    }

    // Constructor
    public Bike(Motercycle motercycle) {
        this.motercycle = motercycle;
    }
}
//configuration file

<bean id="bikeReference" class="com.example.Honda">

//setter injection
<bean id="bike1" class="com.example.Bike">
    <property name="honda" ref="bikeReference" />
</bean>

//constructor injection
<bean id="bike2" class="com.example.Bike">
    <constructor-arg ref="bikeReference" />
</bean>

the constructor-arg e is used for constructor injection, the property element is used for setter injection, .

Now xml based configuration is a older approch. Spring Boot encourages the use of Java based configuration using annotations like @Configuration, @Bean, and @Autowired as it provides more flexibility and is more concise and it is less complex than xml based configuration.


@Component
public class Bike {
    private Motercycle motercycle;

    @Autowired
    public Bike(Motercycle motercycle) {
        this.motercycle = motercycle;
    }

 }

This is how we do dependency injection using java based configuration using @Autowired keyword. we use the @Autowired annotation on the constructor of the Bike class it can be also be used for setter injection. When Spring initializes the Bike Bean, it will automatically inject the appropriate implementation of the Motorcycle interface in this case, the Honda bean .

Remember, for @Autowired to work, make sure you have component scanning enabled in your Spring configuration using @ComponentScan or similar. Spring will automatically discover and inject the appropriate beans based on the declared types

@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
    // Configuration settings
}

By using Java based configuration we no longer need use xml file for the bean configuration. We will learn more about Java base configuration @Autowired , @Qualifier in another article.

In short, Inversion of Control is an architectural principle that tells how components interact within an application, while Dependency Injection is a specific implementation technique that addresses the management of component dependencies there are also other techniques like Event-Driven Architecture, Factory method pattern etc . DI is a means to achieve IoC by allowing components to receive their dependencies from an external source, resulting in more modular, flexible, and maintainable codebases. In Spring spring container also known as the IOC container provides the maintains and injects beans.

I hope you understood the inversion of control and dependency injection. follow me for more engaging software engineering content!

Happy Coding🚀🚀