Mastering Dependency Injection in Spring Boot
The Heart of Spring
Dependency Injection (DI) is the foundation of the Spring ecosystem. It allows for loosely coupled, testable, and maintainable code by moving the responsibility of object creation from the application to the IoC Container.
Core Concepts
1. Inversion of Control (IoC)
Instead of the developer calling new MyService(), the framework instantiates and manages the objects (Beans).
2. Bean Scopes
- Singleton (Default): Only one instance exists per container.
- Prototype: A new instance is created every time it’s requested.
- Request: New instance per HTTP request (Web context only).
- Session: New instance per HTTP session.
Practice Exercise: Demonstrating Life Cycle Scopes
We will create three services with different scopes and observe their behavior within a single request.
Step 1: Define Scoped Beans
@Component
@Scope("singleton")
public class SingletonService {
public String id = UUID.randomUUID().toString();
}
@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestService {
public String id = UUID.randomUUID().toString();
}
@Component
@Scope("prototype")
public class PrototypeService {
public String id = UUID.randomUUID().toString();
}
Step 2: Inject into a Controller
@RestController
public class ScopeController {
@Autowired private SingletonService s1;
@Autowired private SingletonService s2;
@Autowired private PrototypeService p1;
@Autowired private PrototypeService p2;
@Autowired private RequestService r1;
@GetMapping("/test-scope")
public Map<String, String> test() {
return Map.of(
"s1", s1.id, "s2", s2.id, // Will be identical
"p1", p1.id, "p2", p2.id, // Will be different
"r1", r1.id // Will be same for this request
);
}
}
Why This Works
- The ApplicationContext: On startup, Spring scans your
@Componentclasses. It creates one instance for every Singleton. - Proxies: For the
Requestscope, Spring injects a Proxy. When you call a method onr1, the proxy looks up the current HTTP request context and finds the specific bean for that request.
The “Captive Dependency” Danger
A common bug occurs when you inject a shorter-lived bean (like a Prototype) into a longer-lived bean (like a Singleton). Because the Singleton is only initialized once, it will “capture” a single Prototype instance and hold onto it forever, defeating the purpose of the prototype scope. Use Lookup methods or ObjectProvider to fix this.
Summary
Understanding DI scopes is the difference between a high-performing Spring app and one riddled with memory leaks and state-sharing bugs. Choose Singleton for stateless logic and Prototype/Request only when internal state must be isolated.