Spring Core & IoC
Inversion of Control, dependency injection, bean lifecycle, application context
Spring Core is the foundation of the entire Spring ecosystem. Inversion of Control (IoC) and Dependency Injection (DI) flip the traditional model — instead of objects creating their own dependencies, the Spring container creates and injects them. This decoupling makes code testable, modular, and configurable.
Key Points
- IoC Container: the ApplicationContext manages bean lifecycle — creation, wiring, and destruction
- Dependency Injection types: Constructor injection (recommended), Setter injection, Field injection (@Autowired on field — avoid in production)
- Constructor injection is preferred: dependencies are explicit, classes are immutable, and tests can be written without Spring
- @Component / @Service / @Repository / @Controller: stereotype annotations that mark a class as a Spring-managed bean
- @Bean: method-level annotation in a @Configuration class that declares a bean explicitly — used for third-party classes
- Bean scopes: singleton (default — one per container), prototype (new instance per injection), request, session (web-aware)
- Bean lifecycle: @PostConstruct (after properties set) → business use → @PreDestroy (before container closes)
- @Autowired: Spring resolves dependency by type; use @Qualifier("name") or @Primary when multiple candidates exist
- Bean lifecycle callbacks: InitializingBean/afterPropertiesSet() and DisposableBean/destroy() — prefer @PostConstruct/@PreDestroy
Spring IoC: container wires Controller → Service → Repository via constructor injection; bean lifecycle from instantiation to destruction
Spring Core: constructor injection, @Bean configuration, @PostConstruct/@PreDestroy lifecycle
// Constructor injection — preferred
@Service
public class UserService {
private final UserRepository repo;
private final EmailService emailService;
// @Autowired optional when single constructor (Spring 4.3+)
public UserService(UserRepository repo, EmailService emailService) {
this.repo = repo;
this.emailService = emailService;
}
}
// @Configuration + @Bean — for third-party classes
@Configuration
public class AppConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplateBuilder()
.setConnectTimeout(Duration.ofSeconds(5))
.setReadTimeout(Duration.ofSeconds(10))
.build();
}
}
// Lifecycle callbacks
@Component
public class CacheWarmer {
@PostConstruct
public void warmUp() { loadCacheData(); } // runs after DI complete
@PreDestroy
public void flush() { persistCacheToDb(); } // runs before shutdown
}
// Prototype scope — new instance per injection point
@Bean @Scope("prototype")
public ReportBuilder reportBuilder() { return new ReportBuilder(); }Real-World Example
Field injection (@Autowired on a private field) is convenient but prevents testing without a Spring context and hides dependencies. Switching to constructor injection immediately reveals when a class has too many dependencies (more than 4–5 constructor params = a design smell suggesting the class needs to be split). Spring Boot auto-generates constructor injection for Lombok @RequiredArgsConstructor.