Spring Session 1

Understanding Spring Framework: IoC & DI Guide

🌱 Understanding Spring Framework

A Simple Guide to IoC and Dependency Injection

💡 What is This About?

Imagine you're building a car. There are two ways to do it:

😞 Bad Way

You build the engine inside the car. If you want a different engine, you have to break the car and rebuild it.

😊 Good Way

You build the engine separately, then put it in the car. Want a different engine? Just swap it out!

This guide shows you how to write code the "good way" using Spring Framework.

❌ Version 1: The Bad Way (Tightly Coupled Code) DON'T DO THIS

The Code

public class Car {
    private DieselEngine engine;
    
    public Car() {
        engine = new DieselEngine();  // Car creates its own engine
    }
    
    public void start() {
        engine.engineStart();
        System.out.println("Car Starts");
    }
}

What's Wrong Here?

Think of it like this:

  • The Car makes its own engine inside itself
  • If you want a Petrol engine instead, you have to change the Car class
  • The Car is "stuck" with whatever engine it creates

🔋 Real Life Example:

Imagine buying a phone where the battery is glued inside. If the battery dies, you have to buy a new phone! That's tight coupling.

Problems:

  • Can't change easily: Want a different engine? Change the code!
  • Hard to test: Can't check if the car works without a real engine
  • Not flexible: One car, one engine type only

✅ Version 2: The Better Way (Loosely Coupled Code) BETTER!

Step 1: Create an Engine Interface

public interface Engine {
    void engineStart();
}
Think of this as a "contract"

Any engine must have an engineStart() method

Step 2: Create Different Engine Types

public class DieselEngine implements Engine {
    @Override
    public void engineStart() {
        System.out.println("Diesel Engine Start");
    }
}

public class PetrolEngine implements Engine {
    @Override
    public void engineStart() {
        System.out.println("Petrol Engine Start");
    }
}

public class ElectricEngine implements Engine {
    @Override
    public void engineStart() {
        System.out.println("Electric Engine Start");
    }
}

Step 3: Car Accepts Any Engine

public class Car {
    private Engine engine;
    
    // Constructor - engine comes from outside!
    public Car(Engine engine) {
        this.engine = engine;
    }
    
    void start() {
        engine.engineStart();
        System.out.println("Car Starts");
    }
}

Step 4: Using It

public class App {
    public static void main(String[] args) {
        // Create an engine
        Engine dieselEngine = new DieselEngine();
        
        // Give it to the car
        Car myCar = new Car(dieselEngine);
        myCar.start();
        
        // Want a different engine? Easy!
        Engine petrolEngine = new PetrolEngine();
        Car anotherCar = new Car(petrolEngine);
        anotherCar.start();
    }
}

Why Is This Better?

📱 Real Life Example:

Like a phone with a removable battery. Battery dies? Just swap it! Same phone, different battery.

Benefits:

  • Flexible: Use any engine type without changing Car
  • Easy to test: Can give Car a "fake" engine for testing
  • Reusable: Same Car works with Diesel, Petrol, or Electric
This is called "Dependency Injection"

The Car doesn't create the engine, we inject (give) it from outside.

🏭 Version 3: Even Better with Factory Pattern EVEN BETTER!

The Problem

In Version 2, we still have to write this every time:

Engine engine = new DieselEngine();
Car car = new Car(engine);

What if we want to create engines based on a name like "diesel" or "petrol"?

The Solution: Engine Factory

public class EngineFactory {
    
    public static Engine getEngine(String type) {
        
        switch(type.toLowerCase()) {
            case "petrol": 
                return new PetrolEngine();
            case "diesel": 
                return new DieselEngine();
            case "electric": 
                return new ElectricEngine();
            default:
                throw new IllegalArgumentException("Engine not available");
        }
    }
}

Using the Factory

public class App {
    public static void main(String[] args) {
        
        // Just say what type you want!
        Car car1 = new Car(EngineFactory.getEngine("electric"));
        car1.start();
        
        Car car2 = new Car(EngineFactory.getEngine("petrol"));
        car2.start();
    }
}

Why Is This Even Better?

  • Simpler: Just tell the factory "give me a petrol engine"
  • Centralized: All engine creation in one place
  • Easy to add new engines: Add to factory, done!

🌱 Enter Spring Framework: The Ultimate Solution

Now here's the magic: Spring Framework does all this for you automatically!

What is Spring IoC (Inversion of Control)?

Normal Way (You Control)

Engine engine = new DieselEngine();  // YOU create it
Car car = new Car(engine);           // YOU connect them

Spring Way (Spring Controls)

// Spring creates everything 
// and gives you a ready car!
Car car = context.getBean(Car.class);
"Inversion of Control" means:

You don't create objects anymore. Spring does it for you!

What is Dependency Injection (DI)?

💉 Dependency Injection Explained

It's when Spring automatically gives (injects) objects what they need.

Comments