Back to Home

Article

Why Constructor Injection is Preferred for Dependency Injection in Spring Boot

May 13, 2025
By Adrianne Magracia
Reading Time : 5
 minute
s
Why Constructor Injection is Preferred for Dependency Injection in Spring Boot Image Banner
Back to Home

Article

Why Constructor Injection is Preferred for Dependency Injection in Spring Boot

May 13, 2025
By Adrianne Magracia
Reading Time : 5
 minute
s

Spring Boot’s Dependency Injection is fundamental to building maintainable, testable, and loosely coupled applications. Among the various injection methods available, constructor injection has emerged as the recommended approach over field injection with @Autowired. This choice significantly impacts the maintainability, testability, and robustness of your code.

Understanding Dependency Injection in Spring Boot

Dependency Injection is a design pattern where objects receive their dependencies rather than creating them internally. This pattern forms the backbone of Spring’s Inversion of Control container, enabling looser coupling between components and facilitating easier testing.

Spring Boot provides three main approaches to dependency injection: constructor injection, setter injection, and field injection. Each method has distinct characteristics and use cases, but recent developments in Spring have shifted the recommended practices significantly toward constructor injection.

Three Main Approaches to Dependency Injection

Before diving into approaches, let’s establish context. @Autowired is an annotation provided by Spring that enables automatic injection of dependencies into Spring-managed beans. With a single annotation, classes will be connected automatically. This annotation can be applied in three primary ways:

Injection TypeProsConsBest For
FieldSimple, concise codeNot immutable, hard to test, hides
dependencies
Test classes, prototypes
SetterAllows optional dependencies, can
change during lifecycle
Not immutable, potential null valuesRare cases with optional or changing
dependencies
ConstructorImmutable, explicit dependencies, easy
testing
Slightly more verboseMost classes

Field Injection

The simplest form of injection involves annotating class fields directly with @Autowired. It is commonly found in many Spring codebases due to its brevity and simplicity.

@Service
public class UserService {
  @Autowired
  private UserRepository userRepository;

  @Autowired
  private EmailService emailService;

  // Service methods
}

Setter Injection

Another approach uses @Autowired on setter methods, which allows for optional dependencies and can modify dependencies after instantiation.

@Service
public class UserService {
    private UserRepository userRepository;
    private EmailService emailService;

    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

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

}

Constructor Injection

Constructor Injection involves providing dependencies through a class constructor.

@Service
public class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    
    // Service methods
}

With modern versions of Spring, the @Autowired annotation isn’t required for constructor injection as Spring automatically detects and injects dependencies through constructors. This method ensures dependencies are available from the moment an object is instantiated.


Possible Use Case for Field Injection

Creating Tests

One thing I’ve seen field injection being used in Spring is when creating tests. In tests, we typically care less about constructor immutability, and are more focused on easily accessing the components we need to test.

@SpringBootTest
class UserServiceIntegrationTest {
    @Autowired
    private UserService userService;

    @Autowired
    private UserRepository userRepository;

    @MockBean
    private EmailService emailService;

    @Test
    void registerUserShouldSaveUser_WhenUserIsValid() {
        // Given
        User newUser = new User("testuser", "[email protected]");
        when(emailService.sendWelcomeEmail(any(User.class))).thenReturn(true);

        // When
        userService.registerUser(newUser);
        
        // Then
        User savedUser = userRepository.findByUsername("testuser");
        assertNotNull(savedUser);
        assertEquals("[email protected]", savedUser.getEmail());
        verify(emailService).sendWelcomeEmail(any(User.class));
    }
}

Configuration Classes

For classes with many optional dependencies or complex configuration logic, field injection might be appropriate:

@Configuration
public class SecurityConfig {

    @Autowired(required = false)
    private SSLConfigurer sslConfigurer;

    @Autowired(required = false)
    private CustomAuthenticationProvider authProvider;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        // Use optional dependencies if available
        if (sslConfigurer != null) {
            sslConfigurer.configure(http);
        }

        return http.build();
    }
}


Why Constructor Injection is Preferred

Immutability and Null Safety

It allows dependencies to be declared as final fields, ensuring they cannot be changed after initialization and will never be null during the bean’s lifecycle. This provides greater robustness in your application.

@Service
public class UserService {
    private final UserRepository userRepository;
    private final EmailService emailService;

    public UserService(UserRepository userRepository, EmailService emailService){
        this.userRepository = userRepository;
        this.emailService = emailService;
    }

    // Service methods
}

Simplified Testing

It allows dependencies to be mocked or stubbed directly in test constructors without requiring Spring context or reflection based tools.

@Test
public void registerUserShouldCompleteSuccessfully_WhenUserDataIsValid() {
    // Mock dependencies
    UserRepository mockRepository = mock(UserRepository.class);
    EmailService mockEmailService = mock(EmailService.class);

    // Create service with mocked dependencies
    UserService userService = new UserService(mockRepository, mockEmailService);
    
    // Test the service
    userService.registerUser(new User());
}

Official Spring Recommendation

The Spring team now officially recommends constructor injection as the preferred approach. Since Spring Framework 4.3, classes with a single constructor don’t even require the @Autowired annotation, as Spring will automatically use that constructor for dependency injection.


Summary

As things go, it’s always about the context of the environment that we’re working on. When evaluating dependency injection approaches in Spring Boot:

  • Constructor injection offers immutability, explicit dependencies, and easier testing, making it the preferred choice for most components
  • Field injection remains useful for test classes and configurations with many optional dependencies
  • Setter injection can be appropriate when dependencies may change during the bean’s lifecycle

It’s important to know the tradeoffs of one approach versus another and choose what would benefit your codebase the most. For new Spring Boot applications, following the Spring team’s recommendation to prefer constructor injection will generally lead to more robust, maintainable, and testable code.


References

https://docs.spring.io/spring-framework/reference/core/beans/dependencies/factory-collaborators.html

https://medium.com/devdomain/spring-boots-autowired-vs-constructor-injection-a-detailed-guide-1b19970d828e

https://www.linkedin.com/pulse/getting-started-dependency-injection-spring-boot-mohamed-ezzat-iipaf

Share

Audiences

Perspectives