What Is Dependency Injection?
Dependency Injection (DI) is a design pattern in which an object's dependencies — the other objects or services it needs to do its job — are supplied to it from the outside rather than being constructed internally. In Spring Boot, the Spring IoC (Inversion of Control) container is responsible for creating objects, wiring their dependencies together, and managing their entire lifecycle, so application code simply declares what it needs and lets the framework provide it.
Cricket analogy: Like a captain who doesn't grow his own fast bowler but is handed Jasprit Bumrah by the selectors before the match, a Spring bean doesn't build its own dependencies — the IoC container hands them over ready to use.
Why Dependency Injection Matters
DI decouples the definition of a component from the creation of its dependencies, which yields three concrete benefits: testability, because a mock or stub can be substituted for a real dependency without touching production code; single responsibility, because a class no longer needs to know how to construct its collaborators, only how to use them; and flexibility, because swapping an implementation (say, switching from a JPA repository to an in-memory one) requires changing configuration, not the consuming class itself.
Cricket analogy: A batsman practicing against a bowling machine set to 140 kph instead of a real fast bowler is swapping in a test double, the same way a unit test injects a mock PaymentService instead of the real one.
Constructor, Setter, and Field Injection
Spring supports three injection styles. Constructor injection passes dependencies as arguments to the class constructor and is the recommended default because it allows fields to be declared final, makes required dependencies explicit, and fails fast at startup if a dependency is missing. Setter injection uses public setter methods and suits optional dependencies that may be reconfigured after construction. Field injection annotates a field directly with @Autowired and, while concise, is discouraged because it hides dependencies from the constructor signature, prevents immutability, and makes unit testing harder without reflection-based tools.
Cricket analogy: A team announces its full playing XI before the toss, so every role is fixed and known upfront — that's constructor injection; swapping a twelfth man in mid-innings for an injury is more like setter injection's late reconfiguration.
// Constructor injection (recommended)
@Service
public class OrderService {
private final PaymentGateway paymentGateway;
private final InventoryClient inventoryClient;
// With a single constructor, @Autowired is optional since Spring 4.3
public OrderService(PaymentGateway paymentGateway, InventoryClient inventoryClient) {
this.paymentGateway = paymentGateway;
this.inventoryClient = inventoryClient;
}
public void placeOrder(Order order) {
inventoryClient.reserve(order.getItems());
paymentGateway.charge(order.getTotal());
}
}
// Setter injection (optional dependency)
@Service
public class NotificationService {
private SmsClient smsClient;
@Autowired(required = false)
public void setSmsClient(SmsClient smsClient) {
this.smsClient = smsClient;
}
}
// Field injection (discouraged)
@Service
public class LegacyReportService {
@Autowired
private ReportRepository reportRepository; // hard to unit test without reflection
}The Spring IoC Container
The IoC container, most commonly accessed as an ApplicationContext, reads bean definitions from annotations, Java configuration classes, or XML, then instantiates, configures, and assembles beans in the correct dependency order. It resolves the dependency graph at startup, detects circular dependencies, and applies the configured scope (singleton by default) to each bean before the application is ready to serve requests. Because the container owns the object graph, developers rarely call new for their own service or repository classes — they simply ask the container for a bean via injection.
Cricket analogy: An IPL franchise's team management staff schedules training, assigns nets, and slots players into the batting order well before match day, the way the ApplicationContext resolves and wires the entire bean graph before the app starts serving requests.
You rarely need to interact with the ApplicationContext directly. Calling applicationContext.getBean(...) manually is usually a sign that constructor injection should be used instead — reach for the container's API directly only in framework integration code or tests.
- Dependency Injection lets the Spring IoC container supply an object's collaborators instead of the object constructing them itself.
- DI improves testability, enforces single responsibility, and makes swapping implementations a configuration change rather than a code change.
- Constructor injection is the recommended default: it enables immutable final fields and fails fast if a required dependency is missing.
- Setter injection suits optional, reconfigurable dependencies; field injection is discouraged because it hides dependencies and complicates unit testing.
- The ApplicationContext resolves the full bean dependency graph, detects circular dependencies, and applies bean scopes before the app is ready.
- Application code should ask for dependencies via injection rather than calling
neworgetBean()directly.
Practice what you learned
1. Which injection style is recommended by default in modern Spring applications?
2. What is the main drawback of field injection with @Autowired?
3. What component is primarily responsible for resolving and wiring a Spring bean's dependencies?
4. Since which Spring version can @Autowired be omitted on a class with a single constructor?
5. Why does constructor injection support 'fail fast' behavior?
Was this page helpful?
You May Also Like
Spring Beans and Annotations
Understand what a Spring bean is, how stereotype annotations declare them, and how lifecycle and qualifier annotations control their behavior.
Component Scanning
Learn how Spring discovers annotated classes on the classpath and registers them as beans, and how to control scan scope with filters.
Configuration Classes
Learn how @Configuration classes and @Bean methods provide explicit, programmatic bean wiring, and how to compose and conditionally activate them.
Bean Scopes in Spring
Understand the difference between singleton, prototype, and web-aware bean scopes, and when scoped proxies are required.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentgRPC Study Notes
Protocol Buffers · 30 topics
Web DevelopmentFlask Study Notes
Python · 30 topics
Web DevelopmentDjango Study Notes
Python · 30 topics
Web DevelopmentNext.js Study Notes
JavaScript · 30 topics