IoC and Dependency Injection

Basic Spring
≈ 2 min read

A dependency does not have to be created by the object itself

Imagine a service that sends a notification. It needs an object that can send a message. The most direct option is to create it inside the service with new. But then the service decides which implementation to use and becomes tightly coupled to it.

class NotificationService {
    private final MessageSender sender;

    NotificationService(MessageSender sender) {
        this.sender = sender;
    }

    void notifyUser(String message) {
        sender.send(message);
    }
}

Here, NotificationService does not create the sender. It simply declares: “I need a MessageSender to work.” A ready-to-use object is passed from the outside through the constructor. This is Dependency Injection — injecting a dependency.

Where IoC fits in

Inversion of Control is a broader idea. The class no longer controls the whole process of creating and connecting objects. An external mechanism receives that part of the control — in Spring, that is the container. It knows which objects are available, which dependencies they need, and how to assemble a working system from them.

DI is one of the main ways to implement IoC. Instead of a chain of dozens of manual new calls, the container creates objects itself and provides the dependencies they need.

Prefer constructor dependencies. Required dependencies are visible directly in the class signature, the object cannot accidentally be created in an incomplete state, and the class itself is easier to use and test without Spring.

What changes in the way you think

In a Spring application, it is useful to ask not “where should I create this object?” but “which object do I need to do the job?”. Creation and wiring gradually move out of business code and into container configuration. On the next pages, we will see where this container lives and which objects it manages.