Dependency Injection
Master Spring's three DI styles — constructor, setter, and field injection — and understand why constructor injection is the only correct choice for production code.
Last reviewed: January 2025
🔍 What Is It?
Dependency Injection is the mechanism by which the Spring IoC Container provides a bean's dependencies.
Three injection types:
1. Constructor Injection (recommended)
@Service
public class OrderService {
private final OrderRepository repository; // final — immutable
public OrderService(OrderRepository repository) { // Spring calls this
this.repository = repository;
}
}2. Setter Injection (optional dependencies)
@Service
public class NotificationService {
private EmailService emailService; // optional
@Autowired(required = false)
public void setEmailService(EmailService emailService) {
this.emailService = emailService;
}
}3. Field Injection (avoid)
@Service
public class UserService {
@Autowired // Don't do this — untestable, hides dependencies
private UserRepository repository;
}🎯 Why Do We Need It?
DI is what makes Spring applications testable and maintainable:
Without DI (tight coupling):
public class OrderService {
private OrderRepository repository = new JpaOrderRepository(); // hardcoded!
// Can't test without a real database
// Can't swap implementations
}With DI (loose coupling):
public class OrderService {
private final OrderRepository repository; // interface, not implementation
public OrderService(OrderRepository repository) {
this.repository = repository;
}
// In tests: inject MockOrderRepository
// In production: inject JpaOrderRepository
// Change implementations without touching OrderService
}🏗️ What Happens in Real Projects?
In a real enterprise Spring Boot application:
- →Every
@Serviceuses constructor injection to receive its@Repositorydependencies - →Every
@Controlleruses constructor injection to receive its@Servicedependencies - →
@Configurationbeans inject other beans via constructor or method parameters - →Tests inject mock implementations via constructor — no Spring context needed
The pattern is so consistent that Lombok's @RequiredArgsConstructor is commonly used to auto-generate constructors for all final fields.
⚙️ How It Works
Constructor injection resolution:
- Spring needs to create
OrderService - Reads its constructor: requires
OrderRepository - Looks up
OrderRepositorybean in the container - Creates
OrderRepositoryfirst (if not yet created) - Calls
new OrderService(orderRepository) OrderServiceis ready
@Autowired is optional for single-constructor classes:
Since Spring 4.3, if a class has exactly one constructor, Spring automatically uses it for injection — no @Autowired needed. This is the modern style.
@Qualifier for disambiguation:
When multiple beans of the same type exist, use @Qualifier("beanName") to specify which one.
💻 Example
Dependency Injection Patterns — Constructor injection, qualifiers, and testing with DI.
🤔 Common Confusions
❓ Why is field injection (@Autowired on fields) bad?
Three reasons: (1) You can't write unit tests without starting a Spring context — there's no way to inject mocks via constructor. (2) Dependencies are hidden — you can't see what a class needs without reading its internals. (3) You can't make fields final, so dependencies could theoretically change. Constructor injection solves all three.
❓ Do I need @Autowired on my constructor?
Not since Spring 4.3. If your class has exactly one constructor, Spring uses it automatically. @Autowired is only needed if you have multiple constructors and want Spring to use a specific one. Modern code omits @Autowired entirely.
❓ What is the difference between @Autowired, @Inject, and @Resource?
@Autowired is Spring-specific (type-based matching). @Inject is JSR-330 standard (same as @Autowired). @Resource is JSR-250 (name-based matching by default). In Spring Boot projects, use @Autowired or (better) no annotation with constructor injection. @Inject and @Resource are for portability that's rarely needed.
🚨 Common Production Issues
Field injection making unit tests require full Spring context — migrate to constructor injection
Circular dependency via constructor injection — refactor the design; circular deps indicate poor separation of concerns
Too many constructor parameters (>5) — sign the class has too many responsibilities, split it
@Autowired not working because the class is created with "new" instead of by Spring
Optional dependency not handled — use Optional<T> or @Autowired(required=false) with null check
✅ Best Practices
Always use constructor injection — never field injection
Make injected fields final — prevents accidental reassignment
Use Lombok @RequiredArgsConstructor to reduce boilerplate
Program to interfaces, not implementations — inject UserRepository, not JpaUserRepository
If a class has more than 4-5 injected dependencies, it's doing too much — split it
Use @Primary for the default implementation, @Qualifier for specific ones
🏛️ Architect's Note
Constructor injection is not just a style preference — it's an architectural constraint that forces good design. When a class has 8 constructor parameters, it's immediately obvious it violates the Single Responsibility Principle. Field injection hides this problem. Teams that enforce constructor injection naturally write smaller, more focused classes. This is why senior engineers insist on it.
💬 Frequently Asked Questions
🎯 Key Takeaways
🔗 Related Topics
🎯 Practice this topic in interviews
↗ Where this topic fits
Spring Boot Technology Guide
Return to the Spring Boot learning hub.
Backend Engineer Career Path
A structured journey through Core Java, Spring Boot, production backend engineering, and interview preparation.
Full Stack Java Developer Career Path
A guided route through frontend foundations, Java backend engineering, delivery workflows, and interview readiness.
Sources
Sources / References
Use these Spring Boot references to verify the concept in official docs and continue into the most relevant SplashRide pages.
Official documentation
Related SplashRide pages
SplashRide Spring Boot learning path
Browse the full Spring Boot topic collection and learning order on SplashRide.
Spring Boot Interview Prep
Practice Spring Boot interview questions and scenario-based answers.
Backend Engineer Career Path
A structured journey through Core Java, Spring Boot, production backend engineering, and interview preparation.
Full Stack Java Developer Career Path
A guided route through frontend foundations, Java backend engineering, delivery workflows, and interview readiness.
IoC Container
Understand the Spring IoC Container — the heart of Spring that manages object creation, dependency wiring, and bean lifecycle in every enterprise application.
Bean Lifecycle
Understand the complete Spring bean lifecycle — from instantiation through initialization to destruction — and how to hook into each phase for initialization and cleanup.