Spring Framework Tutorial 0/14 lessons ~6 min read Lesson 4

    The IoC Container & Beans

    Inversion of Control is a simple rename of a powerful idea: instead of your objects creating their collaborators with new, a container creates them and hands them over.

    Course progress0%
    Focus
    8 guided sections
    Practice signal
    Examples included
    Career prep
    Foundation builder

    Introduction

    Inversion of Control is a simple rename of a powerful idea: instead of your objects creating their collaborators with new, a container creates them and hands them over. Your code becomes a recipe ("I need a PaymentGateway"); the container is the kitchen that fetches the ingredient.

    In Spring, the container is called the ApplicationContext, and every object it manages is called a bean. The container knows how to build each bean, when to build it, how long to keep it, and how to wire it to other beans. Everything else in Spring — MVC, transactions, security — is just beans plugged into this same container.

    Understanding the topic

    Bean lifecycle in plain English:

    • Spring scans configuration → discovers bean definitions.
    • It resolves the dependency graph and orders the beans for creation.
    • It creates each bean using a constructor (or factory method).
    • It injects the bean's dependencies.
    • It applies any BeanPostProcessors (this is where AOP proxies get added).
    • It calls any @PostConstruct initialisation hook.
    • The bean serves requests for as long as the context lives.
    • On shutdown, @PreDestroy runs and the bean is released.

    ApplicationContext vs BeanFactory: BeanFactory is the bare minimum container; ApplicationContext extends it with event publication, message resolution, environment access and AOP integration. In application code you always use the latter.

    Syntax reference

    Declaring beans with Java configuration:

    java
    @Configuration
    public class AppConfig {
    @Bean
    public PaymentGateway paymentGateway() {
    return new StripePaymentGateway("sk_test_...");
    }
    @Bean
    public CheckoutService checkoutService(PaymentGateway gateway) {
    return new CheckoutService(gateway); // Spring passes the bean above
    }
    }

    Informative example

    Starting the container, using a bean, and observing lifecycle hooks:

    java
    public class Demo {
    public static void main(String[] args) {
    var ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    CheckoutService checkout = ctx.getBean(CheckoutService.class);
    checkout.placeOrder(cart);
    ctx.close(); // @PreDestroy hooks run here
    }
    }
    @Component
    class AuditService {
    @PostConstruct void init() { System.out.println("ready"); }
    @PreDestroy void cleanup(){ System.out.println("bye"); }
    }

    Run this and you will see ready printed during startup and bye printed at shutdown — proof that the container, not your code, is driving the lifecycle.

    Real-world use

    Production apps register hundreds of beans: data sources, HTTP clients, schedulers, caches, message listeners. The container resolves their dependency order automatically, which is why you almost never have to think about startup sequencing in a Spring app.

    Best practices

    • Keep beans stateless wherever possible — they are shared by default.
    • Use @Configuration classes; reach for XML only when maintaining legacy code.
    • Group related beans into focused @Configuration classes (e.g. SecurityConfig, PersistenceConfig).

    Common mistakes

    • Forgetting that singleton beans are shared across threads — never hold per-request state in fields.
    • Calling new on a service inside a bean — you bypass the container and lose DI, AOP, and lifecycle hooks.
    • Doing heavy work in a constructor — it blocks application startup; use @PostConstruct if you must.

    Hands-on exercise

    Try this: add a @PostConstruct method to two beans where one depends on the other. Print a message from each. Confirm that the dependency is created first. Then add a third bean with a @PreDestroy method and verify the shutdown order is the reverse of creation.

    Ready to mark this lesson complete?Track your journey across the entire course.