Loslegen
Javaneer
Zurück zur Stufe
Modul 0·Spring-Grundlagen

Dependency Injection im Detail

Konstruktor-, Setter- und Feld-Injektion; Mehrdeutigkeit mit @Qualifier und @Primary auflösen; und warum die Konstruktor-Injektion gewinnt.

18 Min. LesezeitFortgeschritten

Deutsche Übersetzung in Arbeit

Diese Lektion ist noch nicht ins Deutsche übersetzt und wird daher auf Englisch angezeigt. Der Rest der Seite ist vollständig lokalisiert.

Auf dieser Seite

The container wires objects together through dependency injection (DI) - it injects each bean's collaborators instead of the bean fetching them. Spring supports three styles, but they are not equal. Choosing well is one of the clearest marks of a professional Spring developer.

The three styles

// 1. CONSTRUCTOR injection - the recommended default
@Service
class BookService {
    private final BookRepository repository;

    BookService(BookRepository repository) {   // Spring passes the bean in
        this.repository = repository;
    }
}

// 2. SETTER injection - for genuinely optional dependencies
@Service
class BookService {
    private NotificationClient notifier;

    @Autowired(required = false)
    void setNotifier(NotificationClient notifier) { this.notifier = notifier; }
}

// 3. FIELD injection - concise, but avoid it (see below)
@Service
class BookService {
    @Autowired private BookRepository repository;
}

You can drop @Autowired on constructors

If a bean has a single constructor, Spring uses it automatically - no @Autowired needed. That's why constructor injection reads so cleanly.

Why constructor injection wins

Prefer constructor injection for required dependencies. It gives you three things the others can't:

  • final fields & immutability - the dependency is set once and never null.
  • Fail-fast wiring - a missing dependency is caught the instant the container starts, not at first use with a NullPointerException.
  • Trivial testing - you construct the object with plain new in a unit test and pass a mock, no Spring required.
Constructor injection is a complete recipe card

A constructor that lists every dependency is a recipe that names all its ingredients up front: read it once and you know exactly what the dish requires - and you can't start cooking while an ingredient is missing. Field injection is a recipe that says "grab whatever you need from the pantry as you go" - you only discover a missing ingredient halfway through, at runtime.

Resolving ambiguity

What if two beans could satisfy one dependency? Say two PaymentGateway implementations. Spring can't guess, so it fails - unless you disambiguate:

@Component
@Primary                                   // the default choice when ambiguous
class StripeGateway implements PaymentGateway { }

@Component("legacyGateway")
class PaypalGateway implements PaymentGateway { }

@Service
class Checkout {
    Checkout(@Qualifier("legacyGateway") PaymentGateway gateway) {  // pick explicitly
        // ...
    }
}
  • @Primary marks one bean as the default winner.
  • @Qualifier("name") picks a specific bean by name.

You can even inject all implementations at once - Spring collects every matching bean into a List or Map:

@Service
class NotificationDispatcher {
    private final List<NotificationChannel> channels;   // every channel bean, injected

    NotificationDispatcher(List<NotificationChannel> channels) {
        this.channels = channels;
    }
}

This is a beautifully clean way to implement the Strategy pattern - add a new NotificationChannel bean and it joins the list automatically, no registration.

Make it testable

Here is a class using field injection:

@Service
class BookService {
    @Autowired private BookRepository repository;
    List<Book> featured() { return repository.findFeatured(); }
}

Rewrite it with constructor injection, then write the one line that constructs it in a unit test with a mock repository (no Spring context).

Why is constructor injection preferred over field injection for required dependencies?

Key takeaways

  • Dependency injection means the container supplies a bean's collaborators; Spring offers constructor, setter, and field injection.
  • Prefer constructor injection for required dependencies: it enables final fields, fails fast on missing beans, and makes unit testing trivial.
  • A single constructor needs no @Autowired annotation.
  • Disambiguate multiple candidates with @Primary (default winner) or @Qualifier (pick by name).
  • Injecting a List or Map of an interface gives you every implementing bean - a clean, registration-free Strategy pattern.
War diese Lektion hilfreich?
Diese Seite auf GitHub bearbeiten