Understanding Dependency Injection in Spring: Field vs Constructor vs Setter

Dependency Injection (DI) is at the heart of the Spring Framework. It promotes loose coupling and easier unit testing by decoupling object creation from object usage. In Spring, there are three main ways to inject dependencies into a class: field injection, setter injection, and constructor injection. While all three are supported, each has its own implications for code readability, testability, and maintainability.

In this post, we’ll break down the pros and cons of each, and show how to use them correctly within a modern Spring Boot application.

Field Injection

This is the most concise way to inject a dependency: you annotate the field directly with @Autowired.

@Component
public class PaymentService {

    @Autowired
    private PaymentGateway paymentGateway;

    public void processPayment() {
        paymentGateway.charge();
    }
}

Pros

  • Very concise
  • No need for boilerplate constructors or setters

Cons

  • Harder to test: dependencies are private and not easily overridden in unit tests.
  • Poor encapsulation: fields are injected behind the scenes.
  • Reflection-based: may lead to issues with certain frameworks and tools.

When to use

Field injection should be avoided in favor of constructor injection unless you’re writing quick prototypes or configuration classes.


Setter Injection

Dependencies are provided through setter methods annotated with @Autowired.

@Component
public class NotificationService {

    private EmailService emailService;

    @Autowired
    public void setEmailService(EmailService emailService) {
        this.emailService = emailService;
    }

    public void notifyUser() {
        emailService.sendEmail();
    }
}

Pros

  • Allows optional dependencies (Spring won’t fail if a setter isn’t called unless explicitly required)
  • More testable than field injection

Cons

  • Still permits object creation without all dependencies set
  • Can lead to partially constructed objects
  • More verbose than field injection

When to use

Setter injection is useful for optional dependencies or framework-managed beans (e.g., listeners or configuration objects). Not ideal for mandatory dependencies.


Constructor Injection

This is the most recommended form of injection. Dependencies are provided via constructor parameters, usually with @Autowired (which is optional on a single-constructor class in Spring 4.3+).

@Component
public class OrderService {

    private final InventoryService inventoryService;
    private final PricingService pricingService;

    public OrderService(InventoryService inventoryService, PricingService pricingService) {
        this.inventoryService = inventoryService;
        this.pricingService = pricingService;
    }

    public void placeOrder() {
        if (inventoryService.isInStock() && pricingService.isPriceValid()) {
            // Process order
        }
    }
}

Pros

  • Enforces immutability: dependencies can be final
  • Clear contract: all required dependencies are declared explicitly
  • Ideal for testing: you can inject mocks directly
  • Better support for frameworks like Lombok (@RequiredArgsConstructor)

Cons

  • More verbose (but this is mitigated with tools like Lombok)

When to use

Constructor injection should be the default approach for all required dependencies. It makes the class easier to reason about and unit test.


A Quick Comparison

CriteriaField InjectionSetter InjectionConstructor Injection
TestabilityLowMediumHigh
Required dependenciesNot enforcedNot enforcedEnforced
ImmutabilityNoNoYes
Code verbosityLowMediumMedium (low with Lombok)
Recommended?NoSometimesYes (default)

Final Thoughts

While Spring supports multiple DI styles, constructor injection stands out as the cleanest and most maintainable option. It aligns well with modern practices like immutability, better test coverage, and clearer object lifecycle management.

If you’re building serious applications with Spring Boot, especially in a professional or enterprise setting, embrace constructor injection as your default choice. Use setter injection sparingly, and avoid field injection unless you have a very specific reason.

By mastering the nuances of DI styles, you not only improve your code quality but also become more capable of designing clean, scalable architectures in the Spring ecosystem.

What Is a DTO? (And Why You Shouldn’t Return Your Entities in Spring Boot)

When building REST APIs with Spring Boot, it’s common to see beginners return entities directly from their controllers. At first glance, this seems fine—the data flows, the response looks right, and everything “just works.”

But what happens when your entity evolves? When sensitive fields accidentally get exposed? Or when lazy-loaded relationships break your JSON?

Continue reading “What Is a DTO? (And Why You Shouldn’t Return Your Entities in Spring Boot)”

Getting Started with Spring Boot: Build a Task Manager App from Scratch

Spring Boot is the industry-standard framework for building modern Java applications. It dramatically reduces boilerplate code, accelerates development, and is battle-tested for microservices and monoliths alike. If you’re new to the Spring ecosystem, this post will walk you through everything you need to start building.

Continue reading “Getting Started with Spring Boot: Build a Task Manager App from Scratch”

Spring Boot Profiles: Managing Multiple Environments Like a Pro

Learn how to manage multiple environments in Spring Boot using profiles, properties, and YAML configurations

When developing enterprise applications with Spring Boot, managing multiple environments is a necessity. You need different configurations for development, testing, staging, and production, but hardcoding settings or maintaining separate codebases is a disaster waiting to happen.

Continue reading “Spring Boot Profiles: Managing Multiple Environments Like a Pro”

Protecting Your Spring Boot Application with OAuth2 Resource Server and Auth0

Secure your Spring Boot API with OAuth2 Resource Server and Auth0, using JWT authentication for protection

Security is non-negotiable in modern application development. As APIs become the backbone of digital services, protecting them with robust authentication and authorization mechanisms is critical. OAuth2 has become the de facto standard for securing APIs, and when combined with an identity provider like Auth0, it provides a scalable and secure solution for managing access.

Continue reading “Protecting Your Spring Boot Application with OAuth2 Resource Server and Auth0”

Mastering Request Interception in Spring Boot: Filters vs. Interceptors

Learn the key differences between Filters and Interceptors in Spring Boot and when to use each

When building REST APIs and web applications with Spring Boot, request interception is often necessary to handle authentication, logging, security, request transformation, or other cross-cutting concerns. Spring Boot provides two powerful mechanisms for this: Filters and Interceptors.

Continue reading “Mastering Request Interception in Spring Boot: Filters vs. Interceptors”

Building a Data Pipeline with Apache Camel: Processing Weather Data from AWS SQS

Learn how to build a scalable data pipeline with Apache Camel, AWS SQS, and PostgreSQL – Step by Step

Data pipelines are a critical part of modern software systems, ensuring seamless data flow between various components. In this tutorial, we’ll build an Apache Camel-based data pipeline that listens to an AWS SQS queue, retrieves weather information from the WeatherStack API, and persists the results into a PostgreSQL database.

Continue reading “Building a Data Pipeline with Apache Camel: Processing Weather Data from AWS SQS”

MuleSoft vs Apache Camel: Which Integration Framework Should You Use?

Explore the differences between MuleSoft and Apache Camel to choose the best integration framework

When it comes to building integration solutions for your enterprise applications, choosing the right framework is crucial. Two popular players in the integration space are MuleSoft and Apache Camel. Both offer robust capabilities, but they cater to different needs and use cases. Whether you’re dealing with complex enterprise architecture, microservices, or cloud-based solutions, selecting the best framework can significantly impact the scalability, flexibility, and maintainability of your system.

Continue reading “MuleSoft vs Apache Camel: Which Integration Framework Should You Use?”

OAuth2 for System-to-System Authentication: A Deep Dive into the Client Credentials Flow

Learn about OAuth2 Client Credentials Flow: system-to-system authentication

OAuth2 is the de facto standard for securing APIs and authorizing system-to-system communication. With its wide adoption, you’ve probably encountered it at some point, whether in the context of securing REST APIs, enabling third-party integrations, or simply authenticating users. However, OAuth2 isn’t just a one-size-fits-all protocol; it offers different flows, each tailored to specific use cases. Today, we will focus on one such flow that is often underappreciated but incredibly powerful: the Client Credentials Flow.

Continue reading “OAuth2 for System-to-System Authentication: A Deep Dive into the Client Credentials Flow”

How OAuth2 Differs from API Keys: Understanding Secure API Authentication

Learn the key differences between OAuth2 and API Keys for secure API authentication

In the ever-evolving landscape of software development, securing APIs is non-negotiable. With APIs acting as gateways to sensitive data and critical functionalities, choosing the right authentication method is crucial. Two common approaches dominate the field: API Keys and OAuth2.

Continue reading “How OAuth2 Differs from API Keys: Understanding Secure API Authentication”