CodeGym /Courses /Module 5. Spring /Basics of IoC (Inversion of Control) and the Spring conta...

Basics of IoC (Inversion of Control) and the Spring container

Module 5. Spring
Level 2 , Lesson 0
Available

Now let's dive into one of the key concepts — Inversion of Control (IoC).

Inversion of Control (IoC) is a fundamental concept that underpins the Spring Framework. To get what it means, let's start with a simple analogy.

"Smart" shopping cart at the supermarket

Traditional approach (without IoC):

  • You search for every product in the catalog yourself
  • You remember what you usually buy
  • If an item is missing you look for a substitute manually
  • You track discounts on your favorite items yourself

"Smart" cart (with IoC):

  • The system analyzes your past purchases
  • It automatically suggests a set of products
  • It picks substitutes when something is out of stock
  • It notifies you about deals for your preferences

Why is this IoC?

  1. You hand over control of product selection to the system
  2. The system injects dependencies (products) based on your preferences
  3. If availability changes, you don't need to change your behavior
  4. Easy to test with different preference sets

In code it looks roughly like this:


// Without IoC
class ManualCart {
    private Product milk = new RegularMilk(); // Hardcoded
    private Product bread = new WhiteBread(); // Hardcoded
}

// With IoC
@Component
class SmartCart {
    private final Product milk;  // Spring will inject based on
    private final Product bread; // user's preferences

    public SmartCart(@Qualifier("preferred") Product milk,
                     @Qualifier("preferred") Product bread) {
        this.milk = milk;
        this.bread = bread;
    }
}

In programming, IoC means that the responsibility for creating objects and configuring them is handed to a special "manager" — the IoC container. In Spring, that's ApplicationContext.


How does IoC change the traditional approach to dependency management?

Let's look at a more detailed example. First, we'll implement a car without IoC, the old-school way.

Suppose we have a class Car that needs an Engine to work. In the old approach you'd write something like:


public class Engine {
    public void start() {
        System.out.println("Engine started");
    }
}

public class Car {
    private Engine engine;

    public Car() {
        this.engine = new Engine(); // Hardcoded object creation
    }

    public void start() {
        engine.start();
        System.out.println("Car started");
    }
}

The Car class creates the Engine itself. The problems are obvious:

  1. The Car class then depends on a concrete implementation of Engine.
  2. If the Engine implementation changes, you have to change Car's code.
  3. Testing Car becomes hard because you can't just swap Engine for a test version.

With IoC (the control container)

Now let the Spring container create objects for us:


@Component
public class Engine {
    public void start() {
        System.out.println("Engine started");
    }
}

@Component
public class Car {
    private final Engine engine;

    // Spring will inject the dependency via the constructor
    public Car(Engine engine) {
        this.engine = engine;
    }

    public void start() {
        engine.start();
        System.out.println("Car started");
    }
}

How does this work?

  • We told Spring that Engine and Car are components, using the @Component annotation.
  • Spring creates the Engine object itself and passes it into Car's constructor.
  • If the Engine implementation changes, we only change the Engine code, not Car.

This is Inversion of Control: we no longer control creation and management of dependencies. The Spring container does that for us.


The role of the IoC container in Spring

What exactly does the IoC container do?

The IoC container (in Spring this is ApplicationContext) performs three key tasks:

  1. Creating objects (beans): the container creates instances of all classes marked as components.
  2. Managing dependencies: the container automatically wires dependencies between beans.
  3. Managing lifecycle: the container handles creation, initialization, and destruction of objects.

Types of IoC containers in Spring

  1. BeanFactory: the basic container that creates beans on demand only.
  2. ApplicationContext: a more powerful container that adds extra features (for example, event handling and AOP support).

For most tasks we use ApplicationContext because it offers broader capabilities.


Example of the Spring IoC container in action

Let's build a small example to see how Spring takes over object management.

Step 1: Create beans using the @Component annotation


@Component
public class Engine {
    public void start() {
        System.out.println("Engine started");
    }
}

@Component
public class Car {
    private final Engine engine;

    @Autowired // Tell Spring to inject Engine
    public Car(Engine engine) {
        this.engine = engine;
    }

    public void start() {
        engine.start();
        System.out.println("Car started");
    }
}

Step 2: Configure Spring Boot


@SpringBootApplication
public class SpringIoCExample {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(SpringIoCExample.class, args);

        // Get the Car bean from the container
        Car car = context.getBean(Car.class);
        car.start(); // Start the car
    }
}

Step 3: Execution result


Engine started
Car started

Introduction to key interfaces of the IoC container

BeanFactory

BeanFactory is the most basic Spring IoC container. It creates beans only on request. In real life we rarely use it because of its limited capabilities (for example, no event handling).


BeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml"));

ApplicationContext

ApplicationContext is the standard IoC container in Spring. It adds a lot of useful features:

  • Automatic dependency wiring.
  • Event handling.
  • Support for internationalized messages (i18n).
  • Built-in integration with AOP.

We mostly work with ApplicationContext in the form of Spring Boot applications.


6. Benefits of IoC

Looser coupling Classes don't depend on each other directly, which makes refactoring and swapping parts of the system easier.

Testability You can easily replace real dependencies with mocks for testing.

Scalability The IoC container makes it easy to add and configure new dependencies.

Lifecycle management The container takes care of creating, initializing, and destroying objects.


Common mistakes and pitfalls

When working with IoC you might run into a few common issues:

  • Missing @Component annotation: if you forget to mark a class as a component, Spring simply won't find it.
  • Circular dependencies: if two beans depend on each other, Spring will throw an error.
  • Incorrect configuration: if you forgot to specify the package to scan for components (@ComponentScan), your beans won't be found.

Now that you understand how IoC and its container work in Spring, you've got a powerful tool for building flexible and maintainable applications. Ready to dive into Dependency Injection? Let's move on to the next lecture!

Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION