Lesson 5 / 25

Constructor Injection

Required dependencies, immutable fields.

Prefer constructors over field injection

Declare dependencies as constructor parameters: Spring passes the matching beans when it creates the object (a single constructor needs no @Autowired). Fields can be final, the class cannot exist without its dependencies, and tests can construct it with plain new. Field injection (@Autowired on fields) hides dependencies and makes testing harder. Too many constructor parameters is a sign the class does too much.

A service with constructor injection

This file is from a demo "shop" service generated by start.spring.io for Spring Boot 4.1.1 and Java 21 (Temurin 21.0.12), built with Maven 3.9.16; the project compiled and its tests passed. The repository is injected through the constructor; methods declare transaction boundaries.

package com.example.shop;

import java.math.BigDecimal;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class ProductService {
    private final ProductRepository repository;

    public ProductService(ProductRepository repository) {   // constructor injection
        this.repository = repository;
    }

    @Transactional(readOnly = true)
    public Product get(long id) {
        return repository.findById(id).orElseThrow(() -> new ProductNotFoundException(id));
    }

    @Transactional(readOnly = true)
    public List<Product> cheaperThan(BigDecimal max) {
        return repository.findByPriceLessThanOrderByPriceAsc(max);
    }

    @Transactional
    public Product create(String name, BigDecimal price) {
        return repository.save(new Product(name, price));
    }
}

Keep constructors small

More than four or five dependencies usually means the class should be split.

Quick check: Why is constructor injection preferred?

  • It is the only way Spring works
  • Dependencies are explicit, fields can be final, and tests can use new
  • It makes startup slower on purpose
  • It hides dependencies
Answer

Dependencies are explicit, fields can be final, and tests can use new — Explicit and testable.