Spring Boot + Spring Core

How does Spring Dependency Injection actually work?

Spring creates and manages objects called beans, figures out what each bean depends on, and injects those dependencies when it creates the bean.

Spring CoreDependency InjectionIoCBeans

The Short Answer

Spring Dependency Injection means your class does not create its own dependencies. Instead, it declares what it needs, and Spring provides those dependencies when it creates the object.

The important idea is this: your application code no longer controls object creation directly. Spring's container does.

In plain Java, your object says: “I will create what I need.” In Spring, your object says: “Here is what I need. Please give it to me.”

The Real Problem Spring Solves

Imagine a service class that needs a repository, a validator, a clock, a metrics recorder, and maybe a client for another API.

Without dependency injection, code often turns into this:

java
public class OrderService {
    private final OrderRepository repository = new OrderRepository();
    private final PaymentClient paymentClient = new PaymentClient();
    private final MetricsRecorder metrics = new MetricsRecorder();

    public void placeOrder(Order order) {
        // business logic
    }
}

This looks simple at first, but the class is now tightly coupled to concrete implementations. Testing is harder, replacing dependencies is harder, and configuration becomes scattered everywhere.

Tight Coupling

The service chooses exact implementations instead of depending on abstractions.

Hard Testing

Replacing real dependencies with test doubles becomes awkward.

Scattered Setup

Object creation and configuration get spread across business code.

The Mental Model

Without Spring DI

OrderService
new OrderRepository()
new PaymentClient()
new MetricsRecorder()

The class controls object creation. It is coupled to specific implementations.

With Spring DI

Spring Container
OrderRepository Bean
PaymentClient Bean
MetricsRecorder Bean
Injects into OrderService

Spring creates the dependencies and passes them into your class. Your class focuses on business logic.

What Happens at Startup?

At a high level, Spring Boot starts the application, scans for components, builds bean definitions, creates beans, resolves their dependencies, and wires everything together.

Step 1

Start app

Step 2

Scan components

Step 3

Create bean definitions

Step 4

Resolve dependencies

Step 5

Instantiate beans

Step 6

Inject dependencies

This is why a class annotated with @Service can be injected into a controller without you manually creating it. Spring discovered it, registered it as a bean, and made it available to other beans.

Constructor Injection Example

Constructor injection is usually the cleanest way to express required dependencies.

java
@Service
public class OrderService {
    private final OrderRepository repository;
    private final PaymentClient paymentClient;

    public OrderService(
            OrderRepository repository,
            PaymentClient paymentClient
    ) {
        this.repository = repository;
        this.paymentClient = paymentClient;
    }

    public void placeOrder(Order order) {
        // business logic
    }
}

Notice that OrderService does not call new OrderRepository(). It simply declares what it needs.

Constructor injection makes dependencies obvious, supports immutable fields, and makes the class easier to unit test.

How Does Spring Know What to Inject?

Spring looks inside its application context for a matching bean. If the constructor needs an OrderRepository, Spring searches for a bean of that type.

java
@Repository
public class JpaOrderRepository implements OrderRepository {
    // database logic
}

Because this class is annotated with @Repository, component scanning can discover it and register it as a bean.

@Component, @Service, and @Repository

These annotations all register classes as Spring-managed beans, but they communicate intent.

@Component

Generic Spring-managed component.

@Service

Business/service-layer component.

@Repository

Persistence/data-access component.

For interview purposes, it is useful to say that these are stereotype annotations. They help Spring discover beans and help humans understand the role of a class.

@Bean vs @Component

Use @Component when the class is yours and can be annotated directly.

Use @Bean when you want to create a bean manually, often for third-party classes or configuration-heavy objects.

java
@Configuration
public class AppConfig {

    @Bean
    public Clock clock() {
        return Clock.systemUTC();
    }
}

Here, Clock is not your custom Spring component. You are telling Spring: “Call this method and register the returned object as a bean.”

Common Interview Trap: Is DI Magic?

It can feel like magic because you do not call constructors manually. But the idea is straightforward:

Spring builds an object graph. Each bean is a node. Constructor parameters are edges. Spring figures out the order and wires the graph.
OrderController Bean
OrderService Bean
OrderRepository Bean
PaymentClient Bean

What If There Are Multiple Beans of the Same Type?

If Spring finds exactly one matching bean, injection is simple. If it finds multiple candidates, it needs help deciding.

You can resolve ambiguity with names, qualifiers, or primary beans.

java
@Service
public class NotificationService {
    private final MessageSender sender;

    public NotificationService(
            @Qualifier("emailSender") MessageSender sender
    ) {
        this.sender = sender;
    }
}

Common Interview Follow-Ups

Why is constructor injection preferred?

It makes required dependencies explicit, supports final fields, improves testability, and avoids partially initialized objects.

Does Spring create every object in your application?

No. Spring only manages objects registered as beans. Plain objects created with new are not automatically Spring-managed.

What is the difference between IoC and DI?

IoC is the broader principle: control of object creation is inverted. DI is the common technique Spring uses to implement that principle.

What happens if a dependency is missing?

Spring fails at startup because it cannot construct the bean that requires the missing dependency.

Final Takeaway

Spring DI is not just about avoiding new. It is about moving object creation, wiring, configuration, and lifecycle management into the Spring container so your classes stay focused, testable, and loosely coupled.