Spring Core: Dependency Injection 🌱
The Pizza Shop Story 🍕
Imagine you own a pizza shop. Every day, you need ingredients: cheese, tomato sauce, dough, and toppings.
The Old Way (No DI): You wake up at 4 AM. Drive to the farm for cheese. Go to another farm for tomatoes. Mill your own flour. Exhausting! You’re doing EVERYTHING yourself.
The Smart Way (With DI): Suppliers DELIVER everything to your door. You just focus on making amazing pizzas!
This is Dependency Injection. Someone else gives you what you need. You don’t fetch it yourself.
What is Dependency Injection? 🎁
Think of it like this:
Without DI: “I’ll build my own toys!” With DI: “Mom gives me the toys I need.”
Your class (the child) doesn’t create what it needs. Spring (the parent) provides it.
// WITHOUT DI - Making your own toy
class Child {
Toy toy = new Toy(); // Hard work!
}
// WITH DI - Getting toys delivered
class Child {
Toy toy; // Spring gives this!
}
The Three Ways to Get Deliveries 📦
Spring can deliver dependencies in THREE ways. Like three different delivery methods!
1. Constructor Injection (The Moving Truck)
When you move to a new house, the truck brings EVERYTHING at once. You can’t live there without furniture!
@Service
class PizzaShop {
private final CheeseSupplier cheese;
// Everything arrives when
// shop is built!
public PizzaShop(CheeseSupplier cheese) {
this.cheese = cheese;
}
}
Best for: Things you MUST have. No cheese? No pizza shop!
2. Setter Injection (Package Delivery)
Like getting packages after you’ve moved in. Nice to have, but not urgent.
@Service
class PizzaShop {
private ToppingSupplier toppings;
// Delivered later, when ready
@Autowired
public void setToppings(ToppingSupplier t) {
this.toppings = t;
}
}
Best for: Optional things. No extra toppings? Shop still works!
3. Field Injection (Magic Appearance)
Items just appear in your room. Like magic! Simple but mysterious.
@Service
class PizzaShop {
@Autowired // Poof! It appears!
private CheeseSupplier cheese;
}
Warning: Looks easy but harder to test. Use sparingly!
graph TD A[Dependency Injection Types] --> B[Constructor] A --> C[Setter] A --> D[Field] B --> E[Required items] C --> F[Optional items] D --> G[Quick but risky]
@Autowired: The Magic Word 🪄
@Autowired is like saying “Please deliver this!”
Spring hears you and finds the right thing to deliver.
@Service
class Kitchen {
@Autowired
private Oven oven;
// Spring finds an Oven and delivers it!
}
Where Can You Use @Autowired?
| Location | Example | When to Use |
|---|---|---|
| Field | @Autowired Oven oven |
Quick setup |
| Constructor | On constructor method | Required deps |
| Setter | On setter method | Optional deps |
Pro Tip: With ONE constructor, @Autowired is optional! Spring is smart.
@Service
class Kitchen {
private final Oven oven;
// No @Autowired needed!
public Kitchen(Oven oven) {
this.oven = oven;
}
}
@Qualifier: Choosing Your Supplier 🏷️
What if you have TWO cheese suppliers? Spring gets confused!
@Component
class MozzarellaSupplier implements CheeseSupplier {}
@Component
class CheddarSupplier implements CheeseSupplier {}
Problem: “Which cheese do I deliver?”
Solution: Use @Qualifier to pick one!
@Service
class PizzaShop {
@Autowired
@Qualifier("mozzarellaSupplier")
private CheeseSupplier cheese;
// Now Spring knows: Mozzarella please!
}
It’s like saying: “I want the RED package, not the blue one!”
@Primary: The Default Choice ⭐
Don’t want to pick every time? Mark one as the favorite!
@Component
@Primary // I'm the default!
class MozzarellaSupplier implements CheeseSupplier {}
@Component
class CheddarSupplier implements CheeseSupplier {}
Now Mozzarella is always delivered unless you specifically ask for something else.
graph TD A[Multiple Beans?] --> B{Need specific one?} B -->|Yes| C[@Qualifier] B -->|No| D[@Primary] C --> E[Pick exact bean] D --> F[Use default bean]
@Primary vs @Qualifier
| Feature | @Primary | @Qualifier |
|---|---|---|
| Where | On the bean | On injection point |
| Purpose | Set default | Pick specific |
| Override | Yes, by @Qualifier | N/A |
@Value: Reading the Menu 📋
Sometimes you need settings from a file. Like reading today’s prices from a menu!
application.properties:
pizza.price=12.99
shop.name=Mario's Pizza
shop.open=true
Using @Value:
@Service
class PizzaShop {
@Value("${pizza.price}")
private double price;
@Value("${shop.name}")
private String name;
@Value("${shop.open}")
private boolean isOpen;
}
Default Values (Safety Net)
What if a setting is missing? Provide a backup!
@Value("${pizza.price:9.99}")
private double price;
// If not found, use 9.99
SpEL (Spring Expression Language)
Do math or logic right in the annotation!
@Value("#{${pizza.price} * 1.1}")
private double priceWithTax;
// Calculates price + 10% tax
@Value("#{'${shop.name}'.toUpperCase()}")
private String upperName;
// MARIO'S PIZZA
Circular Dependency: The Chicken-Egg Problem 🐔🥚
Imagine this disaster:
- CheeseSupplier says: “I need PizzaShop to know where to deliver!”
- PizzaShop says: “I need CheeseSupplier to make pizza!”
Who gets created first? NOBODY! They’re stuck waiting for each other.
@Service
class CheeseSupplier {
@Autowired
private PizzaShop shop; // Needs shop!
}
@Service
class PizzaShop {
@Autowired
private CheeseSupplier cheese; // Needs cheese!
}
BOOM! Spring crashes with BeanCurrentlyInCreationException
graph LR A[CheeseSupplier] -->|needs| B[PizzaShop] B -->|needs| A style A fill:#ff6b6b style B fill:#ff6b6b
How to Fix Circular Dependencies
Solution 1: Use @Lazy
Tell one bean to wait. “I’ll get you later!”
@Service
class PizzaShop {
@Autowired
@Lazy // Don't get cheese NOW
private CheeseSupplier cheese;
}
Solution 2: Setter Injection
Let one bean finish first, then connect.
@Service
class CheeseSupplier {
private PizzaShop shop;
@Autowired
public void setShop(PizzaShop shop) {
this.shop = shop;
}
}
Solution 3: Redesign (Best!)
If A needs B and B needs A… maybe they should be ONE thing? Or use a third class!
graph TD A[Circular Dependency?] --> B[Try @Lazy] A --> C[Try Setter Injection] A --> D[Redesign Classes] D --> E[Best Solution!] style E fill:#4caf50
Summary: Your DI Toolkit 🧰
| Tool | Purpose | Example |
|---|---|---|
| Constructor DI | Required deps | public Shop(Cheese c) |
| Setter DI | Optional deps | setToppings(Topping t) |
| Field DI | Quick but risky | @Autowired Oven oven |
| @Autowired | Auto-deliver | Marks injection point |
| @Qualifier | Pick specific bean | @Qualifier("cheddar") |
| @Primary | Set default bean | Mark preferred impl |
| @Value | Read properties | @Value("${price}") |
| @Lazy | Break cycles | Delay loading |
The Big Picture 🖼️
graph TD A[Your Class] --> B[Needs Dependencies] B --> C{How many options?} C -->|One| D[Just @Autowired] C -->|Multiple| E{Need specific?} E -->|Yes| F[@Qualifier] E -->|No| G[@Primary] B --> H{From config?} H -->|Yes| I[@Value]
Remember This! 💡
- DI = Getting deliveries instead of fetching yourself
- Constructor injection for MUST-HAVE items
- @Qualifier picks, @Primary sets default
- @Value reads from config files
- Circular deps = redesign your code!
You’re now ready to let Spring do the heavy lifting. Focus on your business logic, and let the framework handle the wiring!
Happy Coding! 🚀