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 SQLExceptionDataAccessException
Stereotype Identifies persistence layer
JPA repos Auto-implemented from interface

5. Creating Beans with @Controller and @RestController

Example: @Controller returning a view

@Controller
public class HomeController {
  @GetMapping("/") String index(Model m) {
    m.addAttribute("name", "world");
    return "index"; // template
  }
}

Example: @RestController returning JSON

@RestController
@RequestMapping("/api/users")
public class UserApi {
  @GetMapping("/{id}") UserDto byId(@PathVariable long id) { /* … */ }
}

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.