Why interviewers love this one: it looks simple, but your answer shows whether you understand object design, testability, and how Spring actually wires beans.
Question: Constructor vs Setter vs Field Injection.
Answer: These are three ways Spring can give a bean its dependencies. Constructor injection passes required objects when the bean is created, setter injection assigns them later through methods, and field injection writes directly into variables with reflection (reflection means Spring sets the field without calling your code). In Spring Boot, constructor injection is usually the best default, setter injection is good for optional dependencies, and field injection is the least preferred because it hides what the class needs.
Interview-Ready Answer: I prefer constructor injection for required dependencies because it makes the class immutable, easier to test, and impossible to create in a half-broken state. I use setter injection when a dependency is optional or can change later. I avoid field injection in production code because the dependency is hidden, it is harder to unit test, and it relies on reflection. In Spring Boot, my rule is: constructor first, setter second, field only in quick demos or legacy code.
Constructor injection gives the dependency at object creation time. Setter injection gives it after the object exists. Field injection writes directly into a private field using reflection, which means Spring reaches into the object without calling your method.
| Style | Best for | Strength | Trade-off |
|---|---|---|---|
| Constructor | Required deps | Immutable, testable | More explicit |
| Setter | Optional deps | Flexible wiring | Can be null |
| Field | Quick demos | Shortest syntax | Hidden dependency |
@Bean method.@Autowired.@PostConstruct, so the bean becomes ready for use.@Autowired(required = false) or Optional<T> when the bean may not exist.final fields, so you lose a clean immutability pattern.Rule of thumb: required dependency = constructor, optional dependency = setter, hidden dependency = usually a smell.
Imagine an e-commerce checkout service. The TaxCalculator and PaymentGateway are required, so constructor injection keeps the service honest: it cannot even start if those pieces are missing. A FraudRulesEngine might be optional in some environments, so setter injection lets the app run with a safe default when that engine is not configured. One team used field injection in a refund batch worker, then wrote a plain unit test with new instead of Spring. The field stayed null, the job threw NullPointerException, refunds stalled, and logs showed messages like Cannot invoke ... because ... is null. Users saw delayed refunds and support tickets spiked.
package com.example.injectiondemo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@SpringBootApplication
public class InjectionDemoApplication {
public static void main(String[] args) {
SpringApplication.run(InjectionDemoApplication.class, args);
}
@Bean
CommandLineRunner demo(CheckoutService checkoutService,
OptionalBannerService optionalBannerService,
FieldInjectedDiagnostics diagnostics) {
return args -> {
double subtotal = 100.0;
System.out.println("Constructor injection: total = " + checkoutService.finalTotal(subtotal));
System.out.println("Setter injection: " + optionalBannerService.bannerFor("Spring Boot"));
System.out.println("Field injection: " + diagnostics.beanSummary());
// Edge case: plain new() bypasses Spring, so field-injected dependencies stay null.
try {
FieldInjectedDiagnostics broken = new FieldInjectedDiagnostics();
System.out.println(broken.beanSummary());
} catch (NullPointerException ex) {
System.out.println("Manual construction failed as expected: " + ex.getClass().getSimpleName());
}
};
}
}
@Component
class TaxCalculator {
double taxFor(double subtotal) {
return subtotal * 0.10;
}
}
@Service
class CheckoutService {
private final TaxCalculator taxCalculator;
// Constructor injection makes required dependencies explicit and the object valid immediately.
CheckoutService(TaxCalculator taxCalculator) {
this.taxCalculator = taxCalculator;
}
double finalTotal(double subtotal) {
return subtotal + taxCalculator.taxFor(subtotal);
}
}
@Component
class BannerFormatter {
String format(String text) {
return "[formatted] " + text;
}
}
@Service
class OptionalBannerService {
private BannerFormatter formatter;
// Setter injection is useful when the collaborator is optional or swappable.
@Autowired(required = false)
public void setFormatter(BannerFormatter formatter) {
this.formatter = formatter;
}
String bannerFor(String text) {
// If Spring does not supply the bean, we still have a safe fallback.
return formatter == null ? text : formatter.format(text);
}
}
@Component
class FieldInjectedDiagnostics {
// Hidden dependency: the class looks empty, but Spring must inject this field.
@Autowired
private ApplicationContext applicationContext;
String beanSummary() {
return "Spring manages " + applicationContext.getBeanDefinitionCount() + " bean definitions";
}
}Follow-up & Tricky Questions:
final. It also encourages weak design because the object can exist in a broken state.@Autowired.Optional<T>, @Nullable, or a default object carefully. If the dependency is truly optional, many teams still prefer setter injection because it reads more naturally.Tricky / gotcha questions:
final? No. Spring sets field-injected values after construction, and final fields must be assigned during construction.Common Mistakes:
Memory Hook: Build it, then add it, then sneak it in. Constructor injection means the parts are in the box before you close it; setter injection means you add a part after assembly; field injection means you slip the part through a hidden panel.
Cheat Sheet:
Constructor = best default for required dependencies.Setter = good for optional or replaceable dependencies.Field = shortest syntax, weakest design.null if wiring is incomplete.Practice Tasks: