DEV Community

Hunor Vadasz-Perhat
Hunor Vadasz-Perhat

Posted on

πŸ“Œ spring-note-002: Understanding IoC (Inversion of Control) & DI (Dependency Injection)

(Reference: Spring Docs - IoC Container)


πŸ”Ή What is IoC (Inversion of Control)?

πŸ’‘ IoC is a design principle where the control of object creation and management is shifted from the developer to the framework (Spring).

πŸ› οΈ Before IoC: Traditional Object Creation (Manual Control)

public class PirateShip {
    private Captain captain;

    public PirateShip() {
        this.captain = new Captain(); // MANUAL CREATION
    }
}
Enter fullscreen mode Exit fullscreen mode

❌ Problem: The class creates its own dependencies, leading to tight coupling (harder to test & maintain).


⚑ IoC in Action: Spring Controls Object Creation

With IoC, Spring is responsible for creating and injecting dependencies instead of the developer doing it manually.

@Component
public class Captain {
    public String getCommand() {
        return "Set sail, Matey! ☠️";
    }
}

@Component
public class PirateShip {
    private final Captain captain;

    @Autowired
    public PirateShip(Captain captain) {
        this.captain = captain;
    }

    public String sail() {
        return captain.getCommand();
    }
}
Enter fullscreen mode Exit fullscreen mode

πŸ’‘ Spring injects the Captain instance into PirateShip automatically.


πŸ”Ή What is Dependency Injection (DI)?

πŸ’‘ Dependency Injection (DI) is how Spring implements IoCβ€”it automatically provides the required dependencies to objects.

βœ… No new keyword needed.

βœ… Objects are loosely coupled (easier to test & modify).

βœ… Easier configuration via annotations or XML.


πŸ“Œ Types of Dependency Injection in Spring

(Reference: Spring Docs - Dependency Injection)

1️⃣ Constructor Injection (Recommended)

πŸš€ Inject dependencies via constructor (Best Practice).

@Component
public class PirateShip {
    private final Captain captain;

    @Autowired
    public PirateShip(Captain captain) { // Injecting dependency via constructor
        this.captain = captain;
    }
}
Enter fullscreen mode Exit fullscreen mode

βœ… Best for mandatory dependencies.

βœ… Ensures immutability (final fields).


2️⃣ Setter Injection

πŸš€ Inject dependencies via setter methods.

@Component
public class PirateShip {
    private Captain captain;

    @Autowired
    public void setCaptain(Captain captain) { // Setter-based injection
        this.captain = captain;
    }
}
Enter fullscreen mode Exit fullscreen mode

βœ… Good for optional dependencies.

❌ Can lead to mutable objects (less safe).


3️⃣ Field Injection (NOT Recommended)

⚠️ Directly injecting fields using @Autowired.

@Component
public class PirateShip {
    @Autowired
    private Captain captain;
}
Enter fullscreen mode Exit fullscreen mode

❌ Considered a bad practice because:

  • Harder to test (no way to pass a mock in tests).
  • Breaks immutability (makes class state changeable).

πŸ”₯ Best Practice: Use Constructor Injection whenever possible!


πŸ“Œ Hands-On Project: IoC & DI in Action

Step 1: Create a Spring Boot Project

1️⃣ Go to Spring Initializr

2️⃣ Select:

  • Spring Boot Version: Latest stable
  • Dependencies: Spring Web
  • Packaging: Jar 3️⃣ Click Generate and extract the zip file.

Step 2: Define Components

πŸ“Œ Create a Captain component:

package com.example.springdi;

import org.springframework.stereotype.Component;

@Component
public class Captain {
    public String giveOrder() {
        return "All hands on deck, Matey! ☠️";
    }
}
Enter fullscreen mode Exit fullscreen mode

πŸ“Œ Create a PirateShip component that depends on Captain:

package com.example.springdi;

import org.springframework.stereotype.Component;

@Component
public class PirateShip {
    private final Captain captain;

    public PirateShip(Captain captain) {
        this.captain = captain;
    }

    public String sail() {
        return captain.giveOrder();
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Create a REST Controller

πŸ“Œ Expose the PirateShip behavior via an API:

package com.example.springdi.controller;

import com.example.springdi.PirateShip;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/pirates")
public class PirateController {
    private final PirateShip pirateShip;

    public PirateController(PirateShip pirateShip) {
        this.pirateShip = pirateShip;
    }

    @GetMapping("/sail")
    public String sail() {
        return pirateShip.sail();
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Run the Application

πŸ’‘ Run the app using:

mvn spring-boot:run
Enter fullscreen mode Exit fullscreen mode

or

./mvnw spring-boot:run
Enter fullscreen mode Exit fullscreen mode

🌐 Visit: http://localhost:8080/pirates/sail

πŸŽ‰ You should see:

All hands on deck, Matey! ☠️
Enter fullscreen mode Exit fullscreen mode

πŸ“Œ Summary

βœ… IoC = Spring takes control of object creation and lifecycle.

βœ… DI = Injecting dependencies instead of creating them manually.

βœ… Constructor Injection is best practice!

βœ… We built a real Spring Boot app demonstrating DI!

πŸ“Œ Topics Covered in This Section
πŸ“œ IoC & Dependency Injection (βœ”οΈ Covered)

  • What is IoC? (Spring manages object creation, inversion of control).
  • What is DI? (Spring injects dependencies, reducing coupling).
  • Spring’s IoC container (ApplicationContext & BeanFactory).
  • Types of DI: Constructor, Setter, Field Injection (Pros/Cons).
  • Best Practices: Constructor Injection > Setter Injection > Field Injection.

πŸ› οΈ Spring Boot & IoC (βœ”οΈ Covered)

  • Auto-Configuration & @Component Scanning.
  • @Autowired behavior and limitations.
  • How IoC integrates into a Spring Boot project.

πŸ”₯ Key Takeaways You Need to Remember for the Exam:
βœ… Spring’s IoC container creates, configures, and manages objects (beans).
βœ… DI eliminates the need for new keyword by injecting dependencies.
βœ… Spring Boot automatically registers components using @ComponentScan.
βœ… @Autowired is used for injection but Constructor Injection is best practice.

Top comments (0)