Spring Boot: From Basics to Production · Spring Core and Spring Boot Fundamentals

Dependency Injection and Beans

Let Spring create and wire your components through constructors.

Spring's IoC container creates objects called beans and injects their dependencies. Mark classes with stereotypes (@Component, @Service, @Repository, @RestController) or declare beans with @Bean methods in @Configuration classes (useful for third-party types). Prefer constructor injection with final fields — dependencies are explicit, immutable and easy to test. With a single constructor, no @Autowired is needed.

public interface PaymentGateway {
    PaymentResult charge(Money amount, String token);
}

@Service
public class CheckoutService {
    private final OrderRepository orders;
    private final PaymentGateway payments;

    public CheckoutService(OrderRepository orders, PaymentGateway payments) {   // constructor injection
        this.orders = orders;
        this.payments = payments;
    }

    public Order checkout(Cart cart, String token) {
        var result = payments.charge(cart.total(), token);
        return orders.save(Order.paid(cart, result.reference()));
    }
}

@Configuration
class PaymentConfig {
    @Bean
    PaymentGateway paymentGateway(StripeProperties props) {
        return new StripeGateway(props.apiKey());
    }
}

Common pitfalls

  • Field injection (@Autowired private Foo foo;) — hides dependencies and complicates tests.
  • Circular dependencies between services (a design smell; Boot fails fast by default).
  • Multiple beans of the same type without @Primary or @Qualifier.

Key points

  • Beans are container-managed objects
  • Constructor injection with final fields
  • @Bean for third-party classes

Further reading: Spring: Dependency injection

Practice questions

Why is constructor injection preferred over field injection?

Answer

Dependencies are explicit and final, objects can't be created half-initialized, tests can instantiate classes with plain new, and circular dependencies are detected.

How do you resolve two beans implementing the same interface?

Answer

Mark one @Primary, or inject a specific one with @Qualifier("name"), or inject List<Interface>/Map<String, Interface> to get all.

Open in the app to mark lessons complete, take notes and track your progress.