IoC Container
The IoC container is the heart of the Spring Framework. It creates the objects your application needs, wires them together, and manages them for their entire lifetime. Instead of your code constructing and connecting collaborators by hand, you describe what you need and the container assembles it for you.
What is Inversion of Control?
Inversion of Control (IoC) is a design principle in which the control over object creation and wiring is inverted — moved out of your classes and into a framework. In a traditional program a class calls new to build its dependencies. With IoC, the framework constructs those dependencies and hands them to the class.
The most common form of IoC is Dependency Injection (DI): the container injects a class’s collaborators rather than letting the class fetch them. The payoff is loose coupling. A UserController declares that it needs a UserService; it does not care which concrete implementation arrives, so you can swap implementations or inject test doubles without touching the controller.
Note: IoC is the principle; DI is the technique Spring uses to implement it. See Dependency Injection for the injection styles themselves.
The Spring container
The container reads bean definitions — from annotated classes, @Configuration methods, or (historically) XML — instantiates each bean, resolves its dependencies, and stores it in a registry keyed by name and type. In Spring Boot the container is created and configured automatically when you call SpringApplication.run(...).
@SpringBootApplication
public class StoreApplication {
public static void main(String[] args) {
// run() builds the container and returns it
ConfigurableApplicationContext context =
SpringApplication.run(StoreApplication.class, args);
// Retrieve a bean from the container by type
OrderService orders = context.getBean(OrderService.class);
orders.placeOrder("SKU-42");
}
}
The object returned by run() is the IoC container. In a typical web application you rarely touch it directly — Spring injects beans for you — but it is useful for bootstrapping and tooling.
ApplicationContext vs BeanFactory
Spring exposes two container interfaces. BeanFactory is the minimal, lazy core; ApplicationContext extends it with enterprise features and is what you use in practice.
| Feature | BeanFactory | ApplicationContext |
|---|---|---|
| Bean instantiation / wiring | Yes | Yes |
| Bean instantiation timing | Lazy (on first request) | Eager for singletons at startup |
Automatic BeanPostProcessor registration | Manual | Automatic |
Internationalization (MessageSource) | No | Yes |
Event publishing (ApplicationEvent) | No | Yes |
Annotation config (@Autowired, @Value) | Limited | Full |
| Typical use | Memory-constrained / embedded | Default for Spring Boot apps |
Tip: Always prefer
ApplicationContext.BeanFactoryexists mainly for legacy and low-footprint scenarios; Spring Boot wires anApplicationContextfor you.
Common ApplicationContext implementations include AnnotationConfigApplicationContext (Java-config, non-web) and AnnotationConfigServletWebServerApplicationContext (auto-selected for Spring Boot web apps).
How beans are wired
You declare beans either with stereotype annotations picked up by component scanning, or with @Bean factory methods inside a @Configuration class. The container then resolves dependencies by type, injecting them through constructors.
@Service
public class OrderService {
private final InventoryService inventory;
// Single constructor — Spring injects InventoryService automatically
public OrderService(InventoryService inventory) {
this.inventory = inventory;
}
public void placeOrder(String sku) {
if (inventory.isAvailable(sku)) {
System.out.println("Order placed for " + sku);
}
}
}
@Service
public class InventoryService {
public boolean isAvailable(String sku) {
return true;
}
}
When the context starts, it sees both @Service beans, notices that OrderService requires an InventoryService, and supplies the managed instance. No new keyword and no lookup code appear in your business logic.
For types you cannot annotate — third-party classes such as RestClient — register them with a factory method instead:
@Configuration
public class AppConfig {
@Bean
public RestClient restClient() {
return RestClient.create("https://api.example.com");
}
}
The method name (restClient) becomes the bean name, and the returned object is the singleton the container manages.
Looking beans up programmatically
Most of the time you let Spring inject beans, but the context also supports direct lookup, which is handy in main methods, tests, and tooling.
ApplicationContext context =
new AnnotationConfigApplicationContext(AppConfig.class);
// By type
RestClient client = context.getBean(RestClient.class);
// By name
RestClient sameClient = (RestClient) context.getBean("restClient");
// By name and type (preferred — no cast)
RestClient typed = context.getBean("restClient", RestClient.class);
Output:
Bean 'restClient' resolved from container
Singleton instance shared across all lookups: true
Warning: Avoid scattering
getBeancalls through application code — that re-introduces the tight coupling IoC removes. Reserve it for bootstrap and infrastructure code; everywhere else, inject.
Container responsibilities
In summary, the IoC container is responsible for:
- Reading bean definitions from annotations, Java config, and auto-configuration.
- Instantiating beans and resolving constructor/setter dependencies.
- Managing scope — one shared singleton by default, or other scopes on request.
- Driving the lifecycle — running initialization and destruction callbacks.
- Publishing events and exposing infrastructure such as
MessageSource.
In This Section
- Dependency Injection — injection styles and best practices.
- Beans & Lifecycle — how beans are created and destroyed.
- Bean Scopes — singleton, prototype, request, and more.
- Stereotype Annotations —
@Component,@Service,@Repository. - Configuration Beans —
@Configurationand@Beanmethods. - Auto-Configuration — how Spring Boot wires beans for you.