PrepZone Logo
PrepZone

Constructor, Setter, and Field Injection

Three ways to inject dependencies — and why constructor injection is the default choice.

Why this matters

  • Constructor injection is the Spring team's recommended style — it makes BookStore services testable and immutable.
  • Field injection looks shorter but hides dependencies and breaks unit tests that do not boot the full context.
  • Interviewers ask you to compare the three styles; knowing the trade-offs signals production experience.

Constructor injection (preferred)

Java
@Service
public class BookService {
    private final BookRepository repository;
    private final PricingClient pricingClient;

    public BookService(BookRepository repository, PricingClient pricingClient) {
        this.repository = repository;
        this.pricingClient = pricingClient;
    }

    public Book findById(Long id) {
        return repository.findById(id)
            .orElseThrow(() -> new BookNotFoundException(id));
    }
}

With a single constructor, @Autowired is optional since Spring 4.3. Fields are final, dependencies are explicit, and the object is fully initialised after construction.

Setter injection

Java
@Service
public class BookService {
    private BookRepository repository;

    @Autowired
    public void setRepository(BookRepository repository) {
        this.repository = repository;
    }
}

Setters allow changing dependencies after construction — useful for optional dependencies but rare in modern Boot apps. The object is in a partially initialised state between construction and setter call.

Field injection (avoid)

Java
@Service
public class BookService {
    @Autowired
    private BookRepository repository;  // hard to test, not final
}

Field injection uses reflection to set private fields. It works but:

Field injection drawbacks

  • Cannot mark fields final
  • Hides the dependency contract
  • Requires Spring context or reflection in unit tests
  • Violates the principle that required dependencies should be constructor parameters
Injection styles
ConstructorRecommended — final fields
SetterOptional dependencies
FieldAvoid — hard to test
Constructor injection is preferred — dependencies are explicit, immutable, and easy to test.

Style comparison

  • Constructor — Required deps, immutable, testable with new BookService(mockRepo, mockClient).
  • Setter — Optional or reconfigurable deps; object usable before all setters run.
  • Field — Convenient for prototypes; discouraged in production services.

Testing with constructor injection

Constructor injection enables pure unit tests without @SpringBootTest:

Java
@Test
void findByIdReturnsBook() {
    BookRepository mockRepo = mock(BookRepository.class);
    when(mockRepo.findById(1L)).thenReturn(Optional.of(new Book(1L, "Clean Code")));

    BookService service = new BookService(mockRepo, mock(PricingClient.class));
    Book result = service.findById(1L);

    assertEquals("Clean Code", result.getTitle());
}

No Spring context needed — the test constructs the service directly.

Optional dependencies

When a dependency is truly optional, combine constructor injection with Optional or @Nullable:

Java
public BookService(BookRepository repository,
                   @Autowired(required = false) MetricsService metrics) {
    this.repository = repository;
    this.metrics = metrics;
}

Prefer a no-op implementation over null checks scattered through business logic.

Multiple implementations

BookStore might have two NotificationSender implementations:

Java
public interface NotificationSender {
    void send(String message);
}

@Service
@Primary
public class EmailNotificationSender implements NotificationSender { ... }

@Service
public class SlackNotificationSender implements NotificationSender { ... }

@Primary breaks ties when injecting by type. Alternatively:

Java
public OrderService(@Qualifier("slackNotificationSender") NotificationSender sender) {
    this.sender = sender;
}

Injection in non-Spring classes

Factory-created objects outside the container cannot use @Autowired. Pass dependencies through constructors manually or refactor so Spring manages the object.

Lombok shortcut

@RequiredArgsConstructor from Lombok generates a constructor for final fields:

Java
@Service
@RequiredArgsConstructor
public class BookService {
    private final BookRepository repository;
    private final PricingClient pricingClient;
}

Equivalent to writing the constructor yourself — still constructor injection under the hood.

Quick recall

Everything you need if you only revisit this box.

  • Constructor injection is the default choice for required dependencies in BookStore services.
  • A single constructor needs no @Autowired annotation.
  • Field injection works but hides dependencies and prevents final fields.
  • Setter injection suits optional or reconfigurable dependencies.
  • Use @Primary or @Qualifier when multiple beans implement the same interface.
  • Constructor injection enables plain new Service(mock) unit tests without Spring.

Test yourself

Answer these before moving on — recall is what makes it stick.