Foundation · Understand
Why Spring Exists: From new to a Container
Begin with a tightly coupled Java object graph and discover the exact construction, configuration, lifecycle, and testability problems the Spring container solves.
Primary source: Spring Framework 6.2 Reference: The IoC Container
Lesson 1 of 14
1d streakThe Architectural Question
Annotations are impossible to reason about when Spring is introduced as syntax instead of a solution to object construction and application assembly.
What this guide proves
- Object construction
- Inversion of Control
- Dependency Injection
The problem starts with object construction
A Java application is an object graph. Controllers depend on application services, services depend on repositories and clients, and infrastructure objects depend on configuration. The difficult part is not calling a method; it is constructing that graph consistently without making business classes responsible for infrastructure choices.
final class CheckoutService {
private final PaymentGateway gateway =
new StripePaymentGateway(System.getenv("STRIPE_KEY"));
Receipt checkout(Order order) {
return gateway.charge(order.total());
}
}Construction leaked into business code
Active Recall Checkpoint
Why is calling new not itself the problem?
Hint: Think about where construction happens and who chooses the implementation.
Show answer & explanation
new is ordinary Java and Spring uses constructors too. The design problem appears when a business object constructs and configures its own collaborators, coupling policy to assembly.
First fix the design with a composition root
Dependency injection does not require a framework. Accept collaborators through the constructor and assemble the graph at one application boundary. The service becomes explicit, testable, and independent of the selected gateway.
interface PaymentGateway {
Receipt charge(Money amount);
}
final class CheckoutService {
private final PaymentGateway gateway;
CheckoutService(PaymentGateway gateway) {
this.gateway = gateway;
}
}
var gateway = new StripePaymentGateway(stripeKey);
var checkoutService = new CheckoutService(gateway);Architectural Boundary · Do Not Confuse
Dependency Injection
Inversion of Control
Dependency Injection is the concrete technique of supplying collaborators from outside an object. Inversion of Control is the broader design shift: framework or composition code controls assembly and calls application code at defined extension points.
The container automates the composition root
As an application grows, the manual composition root must handle hundreds of objects, configuration values, scopes, lifecycle callbacks, decorators, and conditional integrations. Spring moves that repeated assembly work into an IoC container. You provide configuration metadata; the container turns it into object recipes, resolves dependencies, and manages the resulting objects.
Bean
An object created, assembled, and managed by a Spring IoC container.
BeanDefinition
The container recipe describing a bean type, dependencies, scope, lifecycle, and other creation metadata.
BeanFactory
The foundational container contract for registering definitions and obtaining beans.
ApplicationContext
The application-facing container that adds events, resources, messages, environment integration, and automatic extension discovery.
- 01Application configuration describes the object recipes.
- 02The container registers those recipes as BeanDefinition metadata.
- 03The container creates objects and resolves constructor dependencies.
- 04Lifecycle extensions can validate, initialize, decorate, or proxy the objects.
- 05Application code receives ordinary Java references and calls ordinary methods.
Spring owns assembly—not your domain model
The container should assemble long-lived application collaborators such as services, repositories, clients, and configuration objects. It does not need to own every value created with new. Orders, money values, request DTOs, and temporary algorithm objects normally remain ordinary application objects.
Use it when
The object is an application collaborator with dependencies, configuration, lifecycle, cross-cutting behavior, or a shared scope.
Reconsider when
The object is short-lived domain data, a value object, a request payload, or a local implementation detail with no container responsibility.
Active Recall Checkpoint
If Spring creates a CheckoutService, is the service no longer ordinary Java?
Show answer & explanation
It remains an ordinary Java object. Spring controls how the instance is created, wired, initialized, and possibly wrapped; the class and its method semantics are still Java.
Build the interview answer from the runtime model
A strong 60-second answer
Active Recall Checkpoint
Explain how IoC improves testing without saying “because Spring provides mocks”.
Show answer & explanation
IoC encourages dependencies to be supplied from outside the class. A unit test can therefore construct the class with a small fake or mock collaborator without starting Spring or real infrastructure.
Working Implementation
Code Walkthrough
Wire the same checkout service manually and through an ApplicationContext, then identify who owns construction in each version.
package com.example.core;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
public class ManualVsContainerLab {
public interface PaymentGateway {
String charge(double amount);
}
public static final class StripeGateway implements PaymentGateway {
private final String apiKey;
public StripeGateway(String apiKey) { this.apiKey = apiKey; }
public String charge(double amount) {
return "Charged $" + amount + " via Stripe (key: " + apiKey.substring(0, 4) + "***)";
}
}
public static final class CheckoutService {
private final PaymentGateway gateway;
// Constructor injection: collaborators passed in from outside
public CheckoutService(PaymentGateway gateway) { this.gateway = gateway; }
public String checkout(double amount) { return gateway.charge(amount); }
}
@Configuration
static class AppConfig {
@Bean
PaymentGateway paymentGateway() { return new StripeGateway("sk_live_994821"); }
@Bean
CheckoutService checkoutService(PaymentGateway gateway) { return new CheckoutService(gateway); }
}
public static void main(String[] args) {
// 1. Manual composition root: developer creates and wires objects with new
PaymentGateway manualGateway = new StripeGateway("sk_test_12345");
CheckoutService manualService = new CheckoutService(manualGateway);
System.out.println("Manual: " + manualService.checkout(49.99));
// 2. Container composition root: Spring manages construction, resolution & shutdown
try (var context = new AnnotationConfigApplicationContext(AppConfig.class)) {
CheckoutService containerService = context.getBean(CheckoutService.class);
System.out.println("Container: " + containerService.checkout(99.00));
}
}
}Synthesis & Production Verification
Deliberate Failure Condition
- Break: Let a business service construct its database and email collaborators directly
- Observe: Tests need production-shaped infrastructure and replacing an implementation requires editing business code
- Repair: Move object construction to an explicit composition boundary; Spring can own that boundary without changing ordinary Java semantics.
Production Acceptance Criteria
- Distinguish Inversion of Control from Dependency Injection
- Explain why constructor injection makes dependencies visible
- Describe what the container owns and what application code still owns
Key Takeaway
Spring does not replace Java object construction; it centralizes application assembly. Configuration describes object recipes, and the container creates, wires, initializes, and exposes the resulting beans.
Architectural & Technical Review
Core Interview Questions
Q01What problem does the Spring IoC container solve?Show answer
It centralizes the construction and assembly of application collaborators. Instead of business objects selecting and constructing concrete infrastructure, configuration describes beans and dependencies; the container creates the graph, manages lifecycle and scopes, and applies extension points such as post-processors.
Q02How are Inversion of Control and Dependency Injection different?Show answer
Inversion of Control is the broader transfer of flow or assembly responsibility to framework or composition code. Dependency Injection is the specific technique of supplying an object’s collaborators from outside, usually through its constructor.
Q03Should every Java object be a Spring bean?Show answer
No. Long-lived application collaborators with configuration, lifecycle, shared scope, or cross-cutting behavior are good candidates. Domain values, request payloads, entities, and short-lived local objects normally remain ordinary objects.
Production Pitfalls
Critical Anti-Patterns
- Pitfall: Calling dependency injection an annotation feature — Constructor injection is a Java design technique. Spring automates assembly but does not invent the dependency relationship.
- Pitfall: Turning every value into a bean — Container ownership adds indirection. Keep short-lived domain data and local implementation details outside the context.
Primary Documentation & Specifications
Lesson Primary References
Supplementary Module Resources