Spring Framework Tutorial 0/14 lessons ~6 min read Lesson 5

    Dependency Injection in Practice

    Dependency Injection is the mechanism that makes IoC concrete: the container injects collaborators into your class instead of you fetching them.

    Course progress0%
    Focus
    8 guided sections
    Practice signal
    Examples included
    Career prep
    Foundation builder

    Introduction

    Dependency Injection is the mechanism that makes IoC concrete: the container injects collaborators into your class instead of you fetching them. Spring supports three injection styles — constructor, setter, and field — and the right choice matters more than people think.

    Good DI usage produces classes that feel like plain Java: you can new them up in a unit test, pass in fakes, and assert behaviour without starting Spring. Bad DI usage gives you classes that only work "by magic" and that nobody dares to refactor.

    Understanding the topic

    When to use each style:

    • Constructor injection — for required dependencies. Makes the class immutable and easy to test with plain new.
    • Setter injection — for optional dependencies that may change after construction.
    • Field injection — convenient but hides dependencies and breaks unit testing without Spring. Avoid in new code.

    Wiring tools you will see: @Autowired (optional on a single constructor since Spring 4.3), @Qualifier("name") to disambiguate when multiple beans match a type, and @Primary to mark a default choice.

    Syntax reference

    Constructor injection (recommended):

    java
    @Service
    public class InvoiceService {
    private final PaymentGateway gateway;
    private final InvoiceRepository repo;
    // Since Spring 4.3, @Autowired is optional on a single constructor.
    public InvoiceService(PaymentGateway gateway, InvoiceRepository repo) {
    this.gateway = gateway;
    this.repo = repo;
    }
    }

    Disambiguating when several beans match a type:

    java
    interface Notifier { void send(String to, String msg); }
    @Component("email") class EmailNotifier implements Notifier { /*...*/ }
    @Component("sms") class SmsNotifier implements Notifier { /*...*/ }
    @Service
    class AlertService {
    private final Notifier notifier;
    AlertService(@Qualifier("sms") Notifier notifier) { this.notifier = notifier; }
    }

    Informative example

    Testing without Spring becomes trivial:

    java
    class InvoiceServiceTest {
    @Test
    void charges_and_persists() {
    var gateway = new FakeGateway();
    var repo = new InMemoryInvoiceRepo();
    var service = new InvoiceService(gateway, repo);
    service.charge(new Order("o-1", new Money("USD", 49_00)));
    assertTrue(gateway.lastChargeWas(49_00));
    assertEquals(1, repo.count());
    }
    }

    Notice there is no @SpringBootTest, no annotations, no application context. That speed is the practical payoff of constructor injection.

    Real-world use

    Teams that adopt constructor injection consistently report fewer NullPointerExceptions, faster test suites and easier onboarding — because every class's contract is visible at a glance in its constructor signature.

    Best practices

    • Mark dependencies final and inject through the constructor.
    • Group related collaborators into one focused service instead of injecting 10 things — that is a smell.
    • Use @Qualifier at the injection point, not @Primary, when the choice depends on the consumer.

    Common mistakes

    • Field injection makes circular dependencies easy to introduce and hard to spot.
    • Injecting a prototype bean into a singleton without scope-proxy gives you the same instance forever.
    • Calling setProperty from outside the container — Spring will not run validators or post-processors on values you set yourself.

    Hands-on exercise

    Refactor: take any class in your codebase that uses @Autowired on private fields and rewrite it with constructor injection and final fields. Write one unit test that constructs the class directly (no Spring). Notice how much easier the test is to read.

    Ready to mark this lesson complete?Track your journey across the entire course.