Working with Dependency Injection
1. Understanding Inversion of Control (IoC Container)
| Concept | Description |
|---|---|
| Container | ApplicationContext – manages beans |
| Bean | Object whose lifecycle is managed by container |
| DI | Container injects collaborators instead of new |
| Wiring | By type (default), by name (@Qualifier) |
2. Creating Beans with @Component Annotation
| Stereotype | Semantic Role |
|---|---|
@Component |
Generic Spring-managed bean |
@Service |
Business / domain logic |
@Repository |
Persistence — translates exceptions |
@Controller |
MVC controller (returns view names) |
@RestController |
@Controller + @ResponseBody |
3. Creating Beans with @Service Annotation
Example: Service bean with constructor injection
@Service
public class OrderService {
private final OrderRepository repo;
public OrderService(OrderRepository repo) { this.repo = repo; }
public Order place(NewOrder cmd) { return repo.save(Order.from(cmd)); }
}
| Best Practice | Reason |
|---|---|
| Keep services stateless | Singleton-safe |
| Inject via constructor | Immutable, testable |
| Return domain objects, not entities outside boundary | Decoupling |
4. Creating Beans with @Repository Annotation
| Feature | Effect |
|---|---|
| Exception translation | SQLException → DataAccessException |
| Stereotype | Identifies persistence layer |
| JPA repos | Auto-implemented from interface |
5. Creating Beans with @Controller and @RestController
6. Injecting Dependencies with @Autowired
| Style | Recommended? |
|---|---|
| Constructor (no @Autowired needed since 4.3) | Yes — required deps, immutable |
| Setter | Optional deps |
| Field | Discouraged — hard to test |
7. Using Constructor Injection
Example: Multi-dependency constructor injection
@Service
public class CheckoutService {
private final PaymentGateway gateway;
private final InventoryClient inventory;
public CheckoutService(PaymentGateway g, InventoryClient i) {
this.gateway = g; this.inventory = i;
}
}
| Benefit | Why |
|---|---|
| Final fields | Immutability + thread safety |
| No reflection at test time | Plain Java construction |
| Required deps explicit | Compile-time check |
8. Using Setter Injection
Example: Optional setter injection
@Service
public class NotificationService {
private SmsSender sms; // optional
@Autowired(required = false)
public void setSms(SmsSender sms) { this.sms = sms; }
}
9. Using Field Injection
| Issue | Impact |
|---|---|
| Hidden deps | Class lies about its needs |
| Reflection only | Hard to test without context |
| Mutable state | Cannot use final |
Warning:
@Autowired on fields is discouraged in production
code.10. Resolving Multiple Beans (@Qualifier, @Primary)
Example: Primary and qualifier for multiple caches
@Configuration
class CacheConfig {
@Bean @Primary Cache redis() { return new RedisCache(); }
@Bean("local") Cache caffeine() { return new CaffeineCache(); }
}
@Service
class Reports {
Reports(@Qualifier("local") Cache cache) { /* … */ }
}
| Annotation | Resolution |
|---|---|
@Primary |
Default winner when multiple candidates |
@Qualifier("name") |
Explicit selection by bean name |
| Custom qualifier | Compose @Qualifier meta-annotation |
11. Understanding Bean Scopes
| Scope | Lifecycle |
|---|---|
singleton (default) |
One instance per container |
prototype |
New instance per injection / lookup |
request |
Per HTTP request (web) |
session |
Per HTTP session |
application |
Per ServletContext |
websocket |
Per WebSocket |
12. Using Lazy Initialization (@Lazy)
| Usage | Effect |
|---|---|
@Lazy on bean |
Created on first access |
@Lazy on injection point |
Proxy injected; resolves dep on first call |
spring.main.lazy-initialization=true |
Globally lazy — faster startup, deferred errors |
Note: Global lazy-init may delay validation errors to first request.