Backend engineer path · A request's journey · Topic 5 of 18

The IoC container and dependency injection

Not started

In a Spring application you rarely write new OrderService(...). Instead, an ApplicationContext (the IoC container, for "inversion of control") reads which classes are components, works out what each one needs, creates them in the right order and passes the dependencies in. Your code describes the parts; the container assembles the machine. That one shift is what lets Spring later wrap your objects in proxies for transactions, security and metrics without you changing a line.

Why it matters: Every Spring feature, from transactions to security, works because the container, not your code, creates the objects and decides what they are wired to.

  1. 1You describe, the container builds

    Classes say what they need; the ApplicationContext reads the definitions, creates the objects and passes the dependencies in.

  2. 2Constructor injection first

    Final fields set in the constructor make dependencies explicit, impossible to forget and easy to replace in a test.

  3. 3Wiring is by type

    Spring matches a constructor parameter to a bean of that type. Zero candidates or several candidates are both start-up errors.

  4. 4Failures happen at start-up

    A missing or ambiguous dependency stops the application before it takes a single request, which is exactly when you want to find out.

1

How it works

The explanation, with examples you can run.

Describing beans

There are two common ways to tell the container about an object. Component scanning finds classes annotated with @Component (or its specialisations @Service, @Repository, @Controller) under the package of your @SpringBootApplication class. A @Bean method in a @Configuration class is for objects you can't annotate, such as a client from a library.

@Servicepublic class OrderService {  private final OrderRepository orders;  private final Clock clock;   // One constructor: no @Autowired needed  public OrderService(OrderRepository orders, Clock clock) {    this.orders = orders;    this.clock = clock;  }} @Configurationclass TimeConfig {  @Bean  Clock clock() {    return Clock.systemUTC();  }}

When the context starts it sees that OrderService needs an OrderRepository and a Clock, finds (or first creates) a bean of each type, and calls the constructor. Each object made this way is a bean.

Why constructor injection

Spring can also inject into fields (@Autowired private Foo foo) or setters, but the constructor is the better default:

  • The fields can be final, so the object is complete the moment it exists.
  • A unit test can call the constructor with fakes; no container needed.
  • A constructor with eight parameters is a visible sign the class does too much. Eight injected fields hide it.

When there are several candidates

Matching is by type. If two beans implement the same interface, the container refuses to guess:

@Bean @PrimaryPaymentGateway stripe() { return new StripeGateway(); } @BeanPaymentGateway paypal() { return new PayPalGateway(); } // Gets stripe (the primary) unless it asks by namepublic CheckoutService(@Qualifier("paypal") PaymentGateway gateway) { ... }

You can also ask for List<PaymentGateway> or Map<String, PaymentGateway> to receive all of them, which is a neat way to build plug-in style strategies.

Fail fast

Because the whole graph is built at start-up, a missing bean, an ambiguous type or a cycle stops the application with a message naming the class and the parameter. That is a feature: a broken wiring diagram never reaches production traffic. Since Spring Boot 2.6 circular references are rejected by default too; if two services need each other, a third class usually wants to exist.

2

Check yourself

3 questions. Get them all right to finish this topic.

  1. 1.A class has one constructor and no annotations on it. How does Spring know to inject its parameters?
  2. 2.Two beans implement PaymentGateway and a service asks for a PaymentGateway in its constructor. What happens?
  3. 3.A and B each take the other in their constructor. What does Spring Boot 3 do by default?

0 of 3 right so far. Get all three to finish this topic.