Skip to main content

1.1 Spring Framework & IoC Container

← Back to Chapter 1: Spring & Spring Boot Fundamentals

The core purpose of the Spring Framework is enterprise simplification through Inversion of Control (IoC) and Dependency Injection (DI). By managing component lifecycles, configuration, and dependencies within a dedicated container, Spring decouples domain logic from infrastructural wiring.


1. Inversion of Control (IoC) Principle​

In traditional procedural or object-oriented programming without IoC, a consumer object directly controls the creation, configuration, and lifecycle of its collaborators via the new operator:

// Tightly coupled: OrderService dictates exactly how PaymentProcessor is created
public class OrderService {
private final PaymentProcessor paymentProcessor = new StripePaymentProcessor();
}

With Inversion of Control, this responsibility is inverted and handed over to an external runtime orchestrator (the Spring Container):

// Loosely coupled: OrderService declares its collaborator requirement, container injects it
@Service
public class OrderService {
private final PaymentProcessor paymentProcessor;

public OrderService(PaymentProcessor paymentProcessor) {
this.paymentProcessor = paymentProcessor;
}
}

IoC means the control of creating and managing objects is transferred from the application code to the Spring container.

Architectural Benefits:​

  • Testability: Dependencies can easily be mocked or stubbed during unit testing without launching full infrastructure.
  • Maintainability: Implementations can be swapped via configuration without altering downstream consumers.
  • Separation of Concerns: Business services concentrate purely on domain logic rather than object instantiation graphs.

2. BeanFactory vs ApplicationContext​

Spring provides two central container interfaces for managing beans:

┌──────────────────────────────────────────────┐
│ BeanFactory (org.springframework.beans) │
│ - Lazy initialization by default │
│ - Lightweight memory footprint │
│ - Basic DI & lifecycle support │
└──────────────────────┬───────────────────────┘
│ inherits
┌──────────────────────▼───────────────────────┐
│ ApplicationContext (org.springframework.context)
│ - Eager initialization of singletons │
│ - Enterprise services (AOP, Events, i18n) │
│ - Environment, Profiles, & ResourceLoader │
└──────────────────────────────────────────────┘
FeatureBeanFactoryApplicationContext
Packageorg.springframework.beans.factoryorg.springframework.context
Bean LoadingLazy (instantiates when getBean() called)Eager (instantiates singletons on startup)
Annotation SupportRequires manual post-processorsBuilt-in (@Autowired, @PostConstruct)
Enterprise ServicesMinimalAOP, Internationalization, Events, Transactions
Primary UsageResource-constrained embedded devicesModern Web & Enterprise Spring Boot apps

BeanFactory​

Basic container functionality:

Bean creation
Dependency injection
Bean management

ApplicationContext​

Provides BeanFactory functionality plus:

Event handling
Internationalization
Application configuration
Resource loading
AOP integration
More enterprise features

3. Spring Container Lifecycle Stages​

When the ApplicationContext bootstraps, it executes a rigorous lifecycle sequence:

  1. Resource Loading: Reads configuration sources (Java @Configuration, XML, annotations).
  2. Bean Definition Registration: Scans classpath and registers metadata (BeanDefinition).
  3. BeanFactoryPostProcessors: Runs customizations on bean metadata (e.g., resolving property placeholders).
  4. Bean Instantiation: Instantiates beans using constructors or factory methods.
  5. Dependency Injection: Populates properties and injects collaborating bean references.
  6. Aware Interfaces: Invokes BeanNameAware, BeanFactoryAware, ApplicationContextAware.
  7. BeanPostProcessor (Before Init): Executes postProcessBeforeInitialization for cross-cutting hooks.
  8. Initialization: Invokes @PostConstruct, InitializingBean.afterPropertiesSet(), or custom initMethod.
  9. BeanPostProcessor (After Init): Executes postProcessAfterInitialization (e.g., wrapping target beans in CGLIB/JDK dynamic proxies for AOP and @Transactional).
  10. Ready for Use: Beans are served to satisfy application requests.
  11. Destruction (On Context Close): Invokes @PreDestroy, DisposableBean.destroy(), or custom destroyMethod.