Constructor injection is the Spring team's recommended approach to dependency injection. You declare dependencies as constructor parameters, mark the fields final, and Spring wires everything automatically — no @Autowired annotation required when the class has a single constructor. Field injection (@Autowired directly on a field) still works but is discouraged in production code because it hides dependencies, prevents immutability, and makes the class harder to test without a Spring context.
This guide covers all three injection types — constructor, field, and setter — explains exactly why constructor injection wins for most cases, and highlights the exam traps and interview patterns you need to know. All examples use Spring 6 / Spring Boot 3 with the jakarta.* namespace and Java 17+.
The Short Answer
Spring supports three ways to inject dependencies into a bean: through the constructor, through a setter method, or directly into a field. Constructor injection is the default recommendation because it enforces immutability, guarantees the bean is fully initialized before use, and works in plain unit tests without any Spring infrastructure. Field injection is the shortest to write but the hardest to test and maintain. Setter injection sits in between — use it for genuinely optional dependencies.
- Dependencies passed through the constructor
- Fields can be final → immutable
- No reflection needed
- Works without a Spring context (plain new)
- Catches circular dependencies at startup
- Dependencies injected directly into fields
- Requires @Autowired on each field
- Uses reflection — bypasses access modifiers
- Cannot make fields final
- Hides the dependency list from the constructor
- Dependencies injected via setter methods
- @Autowired on the setter (or parameter)
- Allows optional / reconfigurable dependencies
- Bean is usable before all setters are called
- Used when a dependency has a sensible default
Bottom line: Default to constructor injection. Use setter injection for optional dependencies. Reserve field injection for test classes.
What Is Constructor Injection
Constructor injection means declaring every required dependency as a constructor parameter. Spring resolves each parameter from the IoC container, calls the constructor, and hands you a fully initialized bean. The fields holding those dependencies can (and should) be final, which makes the object immutable after construction.
Since Spring 4.3, if a class has exactly one constructor, Spring uses it for injection automatically — no @Autowired needed. This is the most common pattern in modern Spring applications:
@Service
public class OrderService {
private final PaymentClient paymentClient;
private final InventoryRepository inventory;
// single constructor — Spring injects both parameters automatically
public OrderService(PaymentClient paymentClient,
InventoryRepository inventory) {
this.paymentClient = paymentClient;
this.inventory = inventory;
}
}The constructor makes the dependency list explicit: anyone reading the class sees immediately what it needs. If you add a dependency, you add a constructor parameter — the compiler catches missing arguments, and your IDE auto-completes the wiring. If the constructor grows long, that is a design signal (the class has too many responsibilities), not a reason to switch to field injection.
What Is Field Injection
Field injection means putting @Autowired directly on a field. Spring uses reflection to set the field value after the object is constructed, bypassing the constructor entirely. No setter, no constructor parameter — the dependency appears "magically":
@Service
public class OrderService {
@Autowired
private PaymentClient paymentClient;
@Autowired
private InventoryRepository inventory;
}This is shorter to write, which is why it was popular in older Spring tutorials and codebases. The problems become clear when you try to use the class outside Spring:
- You cannot make fields
final— reflection sets them after construction, so the compiler requires them to be non-final. - You cannot instantiate the class in a plain unit test — there is no constructor or setter to pass a mock through. You need either
@SpringBootTest(slow) or a reflection-based test framework like Mockito's@InjectMocks. - The dependency list is hidden — nothing in the public API reveals what the class needs. Adding a 12th
@Autowiredfield does not trigger any warning; a 12-parameter constructor would.
The Spring documentation explicitly states that constructor injection is preferred and that field injection should be avoided in production code. IntelliJ IDEA flags @Autowired fields with a warning for the same reason.
What Is Setter Injection
Setter injection uses @Autowired on a setter method (or any method, technically). Spring calls the setter after the object is constructed via its default or explicit constructor:
@Service
public class NotificationService {
private EmailSender emailSender;
private SmsSender smsSender; // optional — may not be configured
public NotificationService(EmailSender emailSender) {
this.emailSender = emailSender;
}
@Autowired(required = false)
public void setSmsSender(SmsSender smsSender) {
this.smsSender = smsSender;
}
}Setter injection is the right choice when a dependency is genuinely optional — the class works without it and has a sensible default or fallback. The required = false attribute tells Spring to skip the setter if no matching bean is found, instead of throwing an exception.
Outside that use case, setter injection has the same testability advantages as constructor injection (you can call the setter in a test) but loses immutability — the field cannot be final, and nothing prevents someone from calling the setter again later.
Constructor vs Field vs Setter: Head-to-Head
| Dimension | Constructor | Field (@Autowired) | Setter |
|---|---|---|---|
| Immutability | Fields can be final | Cannot be final | Cannot be final |
| Required by default | Yes — missing bean = startup error | Yes (unless required = false) | Yes (unless required = false) |
| Testable without Spring | Yes — pass mocks via new | No — needs reflection or @SpringBootTest | Yes — call the setter |
| Circular dependency detection | Fails fast at startup | Silently succeeds (masks the cycle) | Silently succeeds |
| Dependency visibility | Explicit in the constructor signature | Hidden in private fields | Visible in setter methods |
| Boilerplate | Medium (constructor + final fields) | Low (one annotation per field) | Medium (setter method per dependency) |
| Spring team recommendation | Recommended | Discouraged | Acceptable for optional deps |
Why Spring Recommends Constructor Injection
The Spring Framework documentation is explicit: "The Spring team generally advocates constructor injection." Here are the five concrete reasons.
1. Immutability — Fields Are final
Constructor-injected dependencies are set once and cannot change. The final keyword enforces this at the language level:
private final OrderRepository repository; // set in constructor, never reassignedAn immutable object is safer in concurrent code, easier to reason about, and cannot be accidentally reconfigured after construction.
2. Guaranteed Complete Initialization
A constructor-injected bean is never in a partially initialized state. Every dependency is resolved and assigned before the constructor returns. With field injection, the object exists briefly with all @Autowired fields set to null — between the constructor call and the reflection-based injection. If any @PostConstruct method or InitializingBean.afterPropertiesSet() runs between those two points, it sees null references.
3. Easy Unit Testing
Constructor injection means you can test the class with plain Java — no Spring context, no annotations, no special test runner:
@Test
void shouldCalculateTotal() {
var mockRepo = mock(OrderRepository.class);
var service = new OrderService(mockRepo); // plain constructor call
// test logic — fast, isolated, no Spring overhead
}With field injection, you need either @SpringBootTest (slow, loads the entire context) or reflection tricks like ReflectionTestUtils.setField().
4. Circular Dependency Detection
When two beans depend on each other through their constructors, Spring cannot create either one without the other already existing. It detects this at startup and throws a BeanCurrentlyInCreationException with a clear message naming both beans. This is a feature: circular dependencies are almost always a design mistake, and catching them early is better than debugging subtle runtime issues later.
Field and setter injection mask the cycle because Spring can create both objects first (constructors run with no dependencies) and then inject fields/setters in a second pass. The circular dependency still exists — it just does not surface until something goes wrong at runtime.
5. Self-Documenting Dependencies
The constructor signature acts as a manifest of everything the class needs:
public OrderService(OrderRepository repo,
PaymentClient payments,
InventoryService inventory,
NotificationService notifications) { ... }Four parameters is visible and manageable. Eight parameters is a code smell. The constructor's length becomes a natural design pressure toward smaller, more focused classes — the Single Responsibility Principle enforced by syntax.
Field injection hides this signal. A class can accumulate a dozen @Autowired fields without any single line looking alarming.
@Autowired Is Optional on Constructors
Since Spring 4.3 (2016), a class with exactly one constructor does not need @Autowired on it. Spring uses that constructor for injection automatically. This is the standard pattern in modern Spring Boot applications:
@Service
public class OrderService {
private final PaymentClient paymentClient;
// no @Autowired — Spring uses this constructor automatically
public OrderService(PaymentClient paymentClient) {
this.paymentClient = paymentClient;
}
}If the class has multiple constructors, Spring does not know which one to use, so you must annotate exactly one with @Autowired:
@Service
public class OrderService {
private final PaymentClient paymentClient;
private final AuditLogger auditLogger;
@Autowired // required: tells Spring which constructor to use
public OrderService(PaymentClient paymentClient, AuditLogger auditLogger) {
this.paymentClient = paymentClient;
this.auditLogger = auditLogger;
}
// secondary constructor for a different context
public OrderService(PaymentClient paymentClient) {
this(paymentClient, new NoOpAuditLogger());
}
}Exam tip: The "implicit
@Autowiredon a single constructor" rule is a common exam question. Remember: one constructor → implicit; multiple constructors → explicit@Autowiredrequired on exactly one.
Code: Same Service, Three Injection Styles
All three examples produce the same OrderService bean at runtime. The difference is how dependencies reach it.
Constructor injection (recommended):
@Service
public class OrderService {
private final OrderRepository repository;
private final PaymentClient paymentClient;
public OrderService(OrderRepository repository,
PaymentClient paymentClient) {
this.repository = repository;
this.paymentClient = paymentClient;
}
public void placeOrder(Order order) {
paymentClient.charge(order.total());
repository.save(order);
}
}Field injection (discouraged):
@Service
public class OrderService {
@Autowired
private OrderRepository repository;
@Autowired
private PaymentClient paymentClient;
public void placeOrder(Order order) {
paymentClient.charge(order.total());
repository.save(order);
}
}Setter injection (for optional dependencies):
@Service
public class OrderService {
private OrderRepository repository;
private PaymentClient paymentClient;
@Autowired
public void setRepository(OrderRepository repository) {
this.repository = repository;
}
@Autowired
public void setPaymentClient(PaymentClient paymentClient) {
this.paymentClient = paymentClient;
}
public void placeOrder(Order order) {
paymentClient.charge(order.total());
repository.save(order);
}
}The constructor version is the only one where both fields are final and the object is immutable.
When Field or Setter Injection Is Acceptable
Constructor injection is the default, but there are situations where the other styles make sense:
Field injection in test classes. Spring's @SpringBootTest and @DataJpaTest inject fields via reflection anyway, and the test class is never instantiated manually. Writing a constructor for a test class adds boilerplate with no benefit:
@SpringBootTest
class OrderServiceIntegrationTest {
@Autowired
private OrderService orderService; // acceptable in tests
}Setter injection for optional dependencies. When a bean works without a dependency and has a sensible fallback:
@Autowired(required = false)
public void setCacheManager(CacheManager cacheManager) {
this.cacheManager = cacheManager; // uses in-memory fallback if absent
}Lombok @RequiredArgsConstructor. Generates a constructor from all final fields, giving you constructor injection with minimal boilerplate. This is a common pattern in Spring Boot projects:
@Service
@RequiredArgsConstructor // Lombok generates the constructor
public class OrderService {
private final OrderRepository repository;
private final PaymentClient paymentClient;
}The generated constructor is a real constructor — Spring treats it exactly like a hand-written one.
Common Mistakes and Exam Traps
Use a constructor parameter. Make the field final. Spring wires it automatically (no @Autowired needed with a single constructor).
Use an @Autowired setter. The bean works without this dependency; Spring calls the setter if a matching bean exists.
Acceptable in @SpringBootTest or test classes where constructor boilerplate adds no value. Avoid in production code.
Default to constructor injection. Use setter injection for genuinely optional dependencies. Reserve field injection for test classes.
Mixing injection styles in one class
Technically legal, but confusing. If some dependencies come through the constructor and others through @Autowired fields, readers cannot tell which are required and which are optional. Pick one style per class — constructor for required, setter for optional — and be consistent.
Assuming @Autowired is always required
A class with one constructor does not need @Autowired. Adding it is harmless but redundant. On the exam, watch for answers that claim @Autowired is mandatory — it is not, since Spring 4.3.
Using @Autowired on multiple constructors
Spring allows @Autowired(required = false) on multiple constructors and picks the one with the most resolvable parameters. This is fragile and rarely used. The standard pattern is: one constructor for injection, annotated with @Autowired only if there are multiple constructors.
Ignoring circular dependency warnings
If Spring throws BeanCurrentlyInCreationException, the fix is to redesign the dependency graph — extract a shared service, use an event, or introduce an interface. Switching to field injection to make the error go away does not fix the circular dependency; it hides it. The @Lazy annotation on one constructor parameter is a legitimate workaround when refactoring is not feasible, but it should be a temporary measure.
@Inject vs @Autowired
Jakarta's @Inject (from jakarta.inject) does the same thing as Spring's @Autowired. Both work on constructors, fields, and setters. @Inject does not have a required attribute — it always requires the dependency. On the exam, know that both annotations exist and are interchangeable for basic injection. In practice, most Spring Boot projects use @Autowired or rely on implicit constructor injection with neither annotation.
Constructor Injection for Interviews and the Exam
This is one of the most common Spring interview questions. The expected answer: "Constructor injection is preferred because it enforces immutability (final fields), guarantees complete initialization, enables testing without Spring, and catches circular dependencies at startup. Field injection hides dependencies and requires reflection." Expand with the implicit-@Autowired rule (single constructor, Spring 4.3+) and mention setter injection for optional dependencies.
For the VMware 2V0-72.22 exam, this falls in the Container, Dependency, and IOC domain. Expect questions about:
- Which injection style makes fields
final - Whether
@Autowiredis required on a single constructor - What happens with circular dependencies under constructor vs field injection
- The difference between
@Autowiredand@Inject
See our Spring interview questions and Spring Boot interview questions for more questions in this format, and follow the Spring Professional preparation guide for a structured study path. Try a few free practice questions to test your understanding.
Frequently Asked Questions
What is the difference between constructor injection and @Autowired?
Constructor injection passes dependencies through the constructor — fields can be final and the class works in plain unit tests. @Autowired on a field injects via reflection after construction, so the field cannot be final and the class requires a Spring context to test. Since Spring 4.3, a single-constructor class does not even need @Autowired — Spring injects the constructor parameters automatically.
Why is field injection not recommended in Spring?
Field injection hides the dependency list (nothing in the public API shows what the class needs), prevents immutability (final fields are not possible), makes unit testing harder (you need reflection or @SpringBootTest), and masks circular dependencies that constructor injection would catch at startup. The Spring documentation and IntelliJ IDEA both recommend constructor injection instead.
Is @Autowired required on a constructor in Spring?
No. Since Spring 4.3, if a class has exactly one constructor, Spring uses it for injection automatically without @Autowired. If the class has multiple constructors, you must annotate one with @Autowired to tell Spring which one to use.
What is the difference between @Autowired and @Inject in Spring?
Both annotations mark an injection point and both work on constructors, fields, and setters. @Autowired is Spring-specific and has a required attribute (defaults to true). @Inject comes from Jakarta CDI (jakarta.inject) and always requires the dependency (no required flag). In a Spring application, they are interchangeable for most use cases.
Can I use Lombok with constructor injection in Spring?
Yes. Lombok's @RequiredArgsConstructor generates a constructor from all final fields. Spring treats the generated constructor exactly like a hand-written one — if it is the only constructor, injection is implicit. This is a widely used pattern in Spring Boot projects to get constructor injection with minimal boilerplate.
Next Steps
Default to constructor injection in production code, make your fields final, and skip the @Autowired annotation when the class has a single constructor. Use setter injection for optional dependencies, and reserve field injection for test classes. For more context on how Spring manages the beans you inject, read the bean lifecycle guide and the bean scopes guide. To understand the difference between @Component and @Bean — two ways to register the beans that get injected — see our dedicated comparison. Then put it all into practice with the free practice questions.