Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
The Spring Core  ›  Lesson

Inversion of Control and Dependency Injection

Dependency Injection 14 minThe container, not your objects, creates collaborators and passes them in, preferably through the constructor.

Inversion of Control and Dependency Injection

In a plain Java program, LoanService would build what it needs itself: this.books = new BookRepository();. That looks harmless until a test wants a fake repository, or production wants one that talks to PostgreSQL instead of memory. The service has welded its own parts in. Spring flips this around.

The rule

Ask for what you need; never build it yourself.

Inversion of Control (IoC) is the big idea: your objects no longer control how their collaborators are created. The IoC container (Spring's application context) does. It reads your classes, creates one object per bean, and decides what goes where.

Dependency Injection (DI) is how the container delivers: it passes each object its dependencies from outside. The preferred form is constructor injection:

@Service
public class LoanService {
    private final BookRepository books;
    private final LoanProperties rules;

    public LoanService(BookRepository books, LoanProperties rules) {
        this.books = books;
        this.rules = rules;
    }
}

LoanService only declares what it needs. At startup, the container sees the constructor, finds a BookRepository bean and a LoanProperties bean, and calls the constructor with them. When a class has exactly one constructor, no @Autowired is needed.

Two other forms exist. Field injection puts @Autowired on a field, and setter injection puts it on a setter. Both work, but a field-injected class cannot be created in a plain test without Spring, and its fields cannot be final. Prefer constructors.

What if two beans fit? If both JpaBookRepository and InMemoryBookRepository implement BookRepository, the container refuses to guess and startup fails. Mark one @Primary, or name the one you want with @Qualifier("inMemoryBookRepository") on the constructor parameter.

The shortcut

For every new inside a class, ask: "Is this a collaborator or a value?" A collaborator (repository, client, service) should be a constructor parameter. A value (a Loan, a LocalDate, a list) you still create with new.

Refactoring LoanServiceStep 1 of 4
  1. Spot the new

    this.books = new BookRepository(); sits inside the constructor.

The failure you will meet most is No qualifying bean of type 'BookRepository' available. It means you asked for something nobody registered as a bean: a missing annotation, or a class outside the packages Spring scans.

Drills

Answer fast — trust the rule.

quick drill

What does "Inversion of Control" mean in Spring?

quick drill

Which injection style is recommended for required dependencies?

quick drill

LoanService has a single constructor taking BookRepository. Does it need @Autowired?

quick drill

Two beans implement BookRepository and LoanService asks for one. What happens with no extra hints?

quick drill

Inside LoanService.borrow, which new is fine to keep?

Remember

  • Ask for what you need; never build it yourself.
  • For each new, ask "collaborator or value?" Collaborators become constructor parameters.
  • Trap: two matching beans and no @Primary or @Qualifier — startup fails.

Practice — level up

0/1 solved

Write your answer, then compare it with a rubric and a model answer.

ExplainRefactor to constructor injection

A class has private MemberClient members = new MemberClient(); and @Autowired private BookRepository books;. Explain what is wrong with each line and show how LoanService should receive both instead.