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.

Image of Docusign

πŸ› οΈ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

Top comments (0)