Episode 2 | Spring Core | Understanding IoC, Dependency Injection & Beans
In Episode 1, I covered the WHY behind Spring — tight coupling, and how Spring takes over creating and providing objects (IoC + DI) instead of classes creating their own dependencies.
This episode picks up from there with the parts I hadn't covered yet: how Spring actually does that under the hood — Beans, the Spring Container, and Component Scanning.
🔑 Keywords → 🧠 Understand → 💡 Why? → 💻 Practice → 🎯 Interview Questions → 🛠️ Project
🔑 Keywords for This Episode
- IoC & Dependency Injection (quick recap)
- Spring Bean
- Spring Container / ApplicationContext
- Component Scanning
1️⃣ Quick Recap: IoC & Dependency Injection
From Episode 1: instead of a class creating its own dependency —
public class TicketService {
private TicketRepository repository;
public TicketService() {
repository = new TicketRepository();
}
}
— Spring creates the dependency and hands it to the class. That's Inversion of Control (IoC).
In code, this usually looks like a constructor parameter:
public class TicketService {
private final TicketRepository repository;
public TicketService(TicketRepository repository) {
this.repository = repository;
}
}
TicketService no longer says "let me create a TicketRepository." It says "I need a TicketRepository" — and Spring supplies one. That act of supplying it is Dependency Injection (DI).
IoC = who's in control of creating/managing objects → Spring.
DI = how a class actually receives what it needs → passed in, not self-created.
That's the recap. Now — where do these objects Spring creates actually come from, and where do they live?
2️⃣ Spring Bean — what Spring actually manages
When Spring creates and manages an object for you, that object is called a Bean. This is the vocabulary you'll see everywhere in Spring code and docs, so it's worth being precise about it.
@Service
public class TicketService {
}
The @Service annotation is a signal to Spring: "this class should be managed by you." When Spring creates an object from this class, that specific object — the one Spring created and is tracking — is a Bean.
Compare that to doing it yourself:
TicketService service = new TicketService();
This service object is a completely ordinary Java object. Spring has no idea it exists. It's not a Bean — Spring can't inject it anywhere, manage its lifecycle, or wire its dependencies, because Spring never created it and doesn't know about it.
So the distinguishing line is simple:
A Spring Bean isn't a special kind of class — it's an object that Spring itself created and is managing. The exact same class can produce a Bean (if Spring creates it) or a plain object (if you create it with
new).
This matters because dependency injection only works on Beans. If TicketRepository isn't a Bean, Spring has nothing to inject into TicketService.
3️⃣ Spring Container (a.k.a. ApplicationContext)
So Beans need to be created and tracked somewhere. That "somewhere" is the Spring Container — in practice, you'll most often see it called the ApplicationContext, which is Spring's concrete implementation of the container.
Concretely, the Container is responsible for:
- Creating Bean instances (calling the constructor, essentially)
- Storing references to every Bean it creates, so it can find them again
-
Figuring out each Bean's dependencies — e.g. it sees
TicketServiceneeds aTicketRepository - Injecting those dependencies — passing the right Bean into the right constructor
- Managing the Bean's lifecycle — when it's created, and when it's cleaned up when the app shuts down
Here's the flow when your app starts:
App starts
↓
Spring Container starts up
↓
Container creates TicketRepository Bean
↓
Container creates TicketService Bean
↓
Container sees TicketService needs a TicketRepository
↓
Container injects the TicketRepository Bean into TicketService
You never call new TicketService(new TicketRepository()) yourself — the Container does this wiring for the entire application, for every Bean, automatically.
Spring Container / ApplicationContext = the running system inside your app that creates every Bean, keeps track of them, and wires their dependencies together.
4️⃣ Component Scanning — how the Container finds classes to manage
This raises an obvious question: the Container can't manage a class it doesn't know exists. So how does it discover TicketService in the first place?
That's Component Scanning. When your Spring app starts, Spring scans your project's packages looking for classes marked with specific annotations:
@Component — generic, "let Spring manage this"
@Service — for service/business-logic classes
@Repository — for data-access classes
@Controller / @RestController — for web-layer classes
These are all technically variations of @Component — Spring treats them the same way for scanning purposes, they just also carry extra meaning (e.g. @Repository also tells Spring to translate database exceptions).
Concretely, the process looks like this:
Spring app starts
↓
Component Scanning runs
↓
Scans your packages for @Component-annotated classes
↓
Finds @Service TicketService
↓
Registers it as a component Spring should manage
↓
Spring Container creates a Bean from it
Without Component Scanning finding the class first, none of the rest happens — the Container can only create and inject Beans for classes it has actually discovered and registered.
Component Scanning finds the classes. The Container creates and wires the Beans. Two separate jobs, both needed.
🧠 Putting It All Together
Component Scanning finds @Component classes
↓
Spring Container creates a Bean for each one
↓
Container figures out each Bean's dependencies
↓
Container injects those dependencies (DI)
↓
Your classes never had to create anything themselves (IoC)
That's the full picture: IoC is the principle (Spring is in control), DI is how a class receives what it needs, Beans are what gets created and managed, the Container is where that happens, and Component Scanning is how the Container finds what to manage.
🎯 Interview Check
- What is a Spring Bean, and how is it different from a plain Java object created with
new? - What is the Spring Container (ApplicationContext) actually responsible for?
- How does Spring discover which classes should become Beans?
- If a class isn't found by Component Scanning, can it still be injected as a dependency? Why not?
- What's the relationship between
@Component,@Service,@Repository, and@Controller?
🛠️ Applying This to the Project
Time to take this out of theory. In the Employee Support / Service Request app, I'll annotate the classes:
@RestController TicketController
@Service TicketService
@Repository TicketRepository
and let Component Scanning + the Container find them, create their Beans, and wire the dependencies — instead of me creating any of it manually.
📌 Next Episode — Spring Boot
With the Spring Core foundation in place, next I'll move into the Spring Boot side:
- Auto-configuration
- Starter Dependencies
- Embedded Server / Tomcat
@SpringBootApplicationSpringApplication.run()
Understand → Practice → Interview → Build.
One concept at a time. One project throughout the journey.
Top comments (0)