Spring IoC Container & Dependency Injection
Spring's IoC (Inversion of Control) container manages bean creation, wiring, and lifecycle. DI (Dependency Injection) means beans declare their dependencies; Spring fulfills them rather than the bean constructing them directly.
IoC = a restaurant where you don't cook; you just say 'I need pasta' and the kitchen (Spring container) delivers it to your table. DI = the waiter bringing you ingredients you listed on the menu, not you raiding the fridge.
Three DI styles: constructor injection (recommended — immutable, testable, fails fast at startup if dependency is missing), setter injection (optional dependencies), field injection (@Autowired on a field — discouraged, makes testing harder, hides dependencies). Bean scopes: singleton (default, one instance per container), prototype (new instance per request), request/session/application (web-scoped). @Component/@Service/@Repository/@Controller are all @Component specializations scanned by @ComponentScan. @Configuration + @Bean defines beans programmatically.
BeanFactory is the core container; ApplicationContext extends it with event publishing, i18n, resource loading, and eager singleton initialization. The BeanDefinition is a metadata descriptor of a bean. BeanPostProcessor and BeanFactoryPostProcessor hooks allow modifying bean definitions and instances at different lifecycle phases — Spring AOP, @Autowired processing, and @Value injection are all implemented as BeanPostProcessors. Circular dependency: constructor injection circular dependencies fail at startup (good); setter/field injection circular dependencies are resolved by Spring (risky — partially constructed beans). Spring Boot's auto-configuration uses @Conditional and @Import to conditionally register beans based on classpath and properties.
Spring's IoC container creates and wires beans based on configuration (annotations, XML, or Java config). Prefer constructor injection: it makes dependencies explicit, enables immutable beans, and causes fast startup failure when a dependency is missing. @Autowired by type first, then by name (@Qualifier) if multiple candidates exist. Singleton scope means one instance is shared across the application — stateful fields in singleton beans are a concurrency hazard. Use prototype scope for stateful beans that must be independent per consumer.
Injecting a prototype-scoped bean into a singleton bean defeats prototype semantics — the singleton holds one reference that never changes. Use ApplicationContext.getBean(), a Provider<T>, or @Lookup to get a fresh prototype on each use.