About Us Contact Us Write for Us Advertise
Home > Java > Spring IoC & Dependency Injection Explained (Beginner Guide)
Java

Spring IoC & Dependency Injection Explained (Beginner Guide)

Understand Inversion of Control and Dependency Injection in Spring, the concept that unlocks all of Spring Boot. Plain-English explanation with clear Java examples.

Shiv Pandey
Shiv Pandey
Oct 03, 2026 | 36 views
Spring IoC & Dependency Injection Explained (Beginner Guide)

If there's one concept that unlocks all of Spring, it's this one: Inversion of Control (IoC) and its practical form, Dependency Injection (DI). It sounds intimidating, but the core idea is genuinely simple — and once it clicks, everything else in Spring Boot makes sense. This lesson takes it slow, with plenty of plain-English explanation, because getting this right pays off in every future lesson.

The problem: objects that create their own dependencies

In real apps, objects need other objects to do their work. A UserService might need a UserRepository to talk to the database. The naive approach is to have the service create what it needs:

class UserService {
    // UserService creates its OWN dependency — this is the problem
    private UserRepository repo = new UserRepository();
}

This looks fine, but it's rigid. The service is welded to that exact repository. Want to swap in a different implementation? Or a fake one for testing? You can't, without editing the class. The object controls its own dependencies — and that turns out to be a trap.

The solution: give objects their dependencies

What if, instead of creating its dependency, the service just asks for one — and someone hands it in from outside?

class UserService {
    private final UserRepository repo;

    // the dependency is GIVEN via the constructor, not created inside
    UserService(UserRepository repo) {
        this.repo = repo;
    }
}

That's Dependency Injection: the dependency is "injected" from outside rather than created inside. Now the service doesn't care which repository it gets — you can pass a real one in production and a fake one in tests. It's flexible, testable, and decoupled.

So what's "Inversion of Control"?

Inversion of Control is the big-picture name for this shift. Normally, your code controls creating objects. With IoC, you invert that control — you hand it to a framework. Instead of you writing new UserRepository() everywhere, Spring creates the objects and injects them where needed.

A helpful analogy: think of a restaurant. Without IoC, you're a chef who also grows the vegetables, raises the chickens, and mills the flour — you create everything yourself. With IoC, ingredients are delivered to your kitchen; you just cook. Spring is the delivery service that brings your objects the dependencies they need, so each object can focus on its own job.

Enter the Spring IoC Container

Spring's IoC container (also called the "application context") is the engine that makes this happen. At startup, Spring:

  1. Finds all the classes you've marked as Spring-managed components,
  2. Creates one instance of each (these managed objects are called beans),
  3. Figures out which beans depend on which, and injects them automatically.

You just declare what you need; Spring wires the whole graph together for you.

How it looks in Spring Boot

You mark a class as a Spring-managed bean with an annotation like @Service, @Repository, or @Component. Then you declare dependencies in the constructor, and Spring injects them — no new required:

@Repository
class UserRepository { /* talks to the database */ }

@Service
class UserService {
    private final UserRepository repo;

    // Spring sees this constructor and injects a UserRepository bean automatically
    UserService(UserRepository repo) {
        this.repo = repo;
    }
}

Spring notices UserService needs a UserRepository, finds the bean it created, and passes it in. You never write new for either one.

Best practice — constructor injection: always inject dependencies through the constructor (as above), not by putting @Autowired on fields. Constructor injection makes dependencies explicit, lets you mark them final, and keeps classes easy to test. It's the style modern Spring teams use — and the one this website's code uses throughout.

Dependency injection was the concept that made Spring finally "click" for me — and honestly, until it did, everything else felt like magic incantations. The mental unlock was realising I no longer had to think about creating objects at all; I just declare "this service needs that repository," and Spring assembles everything at startup. In this website's codebase, controllers receive services, services receive repositories, all injected through constructors — I almost never write new for my own components. Once you internalise "don't create your dependencies, declare them," you're thinking the Spring way. This is the foundation the next 16 lessons build on.

Key takeaways

  • Dependency Injection: objects receive their dependencies from outside instead of creating them — flexible and testable.
  • Inversion of Control: you hand object-creation to the framework; Spring creates and wires objects for you.
  • Spring-managed objects are called beans, created and connected by the IoC container at startup.
  • Mark beans with @Service/@Repository/@Component; prefer constructor injection.

← Previous: Spring Lesson 1 — What is Spring Boot?
Next: Spring Lesson 3 — Your First Spring Boot App →
↑ Back to the Spring Boot roadmap

Related Articles

Essential Spring Boot Annotations Explained (Beginner Guide)
Java

Essential Spring Boot Annotations Explained (Beginner Guide)

== vs .equals() in Java: The Difference Explained (Beginner Guide)
Java

== vs .equals() in Java: The Difference Explained (Beginner Guide)

JDK vs JRE vs JVM: The Difference Explained (Beginner Guide)
Java

JDK vs JRE vs JVM: The Difference Explained (Beginner Guide)

Spring Boot Project Ideas (Beginner to Advanced)
Java

Spring Boot Project Ideas (Beginner to Advanced)