Which DI Injection Method Should You Use in Production? Field vs Setter vs Constructor

The article compares Spring's three dependency‑injection styles—field, setter, and constructor—showing code, execution order, source‑code mechanics, risks such as null‑pointer windows and reflection tampering, and concludes that constructor injection (preferably with Lombok @RequiredArgsConstructor) is the production‑grade choice.

Java Tech Workshop
Java Tech Workshop
Java Tech Workshop
Which DI Injection Method Should You Use in Production? Field vs Setter vs Constructor

Preliminary Unified Environment

All beans are defined in a single shared context so that every injection style uses the same instance, eliminating environment differences.

Supported Spring versions: Spring Boot 2.7.15 (Spring Framework 5.3) and Spring Boot 3.2 (Spring Framework 6.1).

1. Three Injection Methods – Code, Execution Sequence, Core Mechanism

1.1 Field Injection

Standard code

@Service
public class UserService {
    @Autowired
    private UserDao userDao;

    public void addUser() {
        userDao.insertUser();
    }
}

Execution sequence

1. Spring creates an empty UserService instance via the no‑arg constructor.
2. The UserDao singleton bean is instantiated early.
3. AutowiredAnnotationBeanPostProcessor uses reflection to set the private field.
4. Dependency is ready and the method can be invoked.

Core source snippet

Field field = UserService.class.getDeclaredField("userDao");
field.setAccessible(true); // break private access
field.set(userServiceObj, userDaoBean);

Because the object is instantiated before the field is set, there is a brief window where the field is null, creating a potential NullPointerException risk.

1.2 Setter Injection

Standard code

@Service
public class UserService {
    private UserDao userDao;

    @Autowired(required = false) // optional dependency
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}

Execution sequence

Instantiate an empty UserService via the no‑arg constructor.

Bean post‑processor scans all @Autowired methods.

Container retrieves the UserDao bean and invokes the setter via reflection.

The sequence mirrors field injection: object creation first, dependency assignment later.

1.3 Constructor Injection

Hand‑written standard

@Service
public class UserService {
    private final UserDao userDao;

    // Single constructor; @Autowired is optional since Spring 4.3
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }
}

Lombok version

@Service
@RequiredArgsConstructor
public class UserService {
    private final UserDao userDao;
}

Execution sequence

1. All dependent beans (e.g., UserDao) are instantiated first.
2. Spring invokes the constructor with ready dependencies, creating the UserService instance atomically.

Object creation and dependency assignment happen atomically, eliminating the null‑pointer window.

2. Comparison of Injection Styles

Dependency assignment timing : Field – after bean instantiation (post‑process); Setter – after bean instantiation (post‑process); Constructor – during bean instantiation (constructor).

final keyword support : Field – not supported (reflection can modify even final fields); Setter – not supported (setter cannot reassign final); Constructor – fully supported, naturally thread‑safe.

Circular‑dependency handling (singleton) : Field – ✅ supported via three‑level cache; Setter – ✅ supported via three‑level cache; Constructor – ❌ throws BeanCurrentlyInCreationException.

Unit testing without container : Field – ❌ requires Spring context; Setter – ⭕ manual new + setter (verbose); Constructor – ✅ direct new with constructor, zero dependencies.

Dependency tampering risk : Field – high (private field can be overwritten via reflection); Setter – high (setter can be called repeatedly); Constructor – zero (final prevents modification).

Official & Alibaba guidelines : Field – explicitly prohibited, deprecated in Spring 6; Setter – allowed only for optional dependencies; Constructor – globally enforced as the preferred approach.

Bean @PostConstruct timing : All three run after dependency injection; with constructor injection the timing is synchronized with object creation.

3. Precautions

3.1 Real‑world field‑injection failures

NullPointerException inside @PostConstruct – @PostConstruct executes before the field‑injection post‑processor; under high concurrency the field may still be null, causing NPE.

Data corruption via reflection – An AOP monitoring component reflected over all bean fields and unintentionally overwrote the UserDao instance. Field injection permits such modification, while constructor‑injected final fields block it.

3.2 Lombok @RequiredArgsConstructor considerations

@AllArgsConstructor

generates a constructor for **all** fields (including non‑final and static), which can easily trigger circular‑dependency startup failures. @RequiredArgsConstructor generates a constructor only for final and @NonNull fields, aligning with Spring best practices and is the production‑grade choice.

Production rule: forbid @AllArgsConstructor for dependency injection; always use @RequiredArgsConstructor .

3.3 @Autowired vs @Resource

@Autowired

: Spring native annotation, matches by type, supports only field/setter injection, does not downgrade to constructor injection. @Resource: JSR‑250 official annotation, matches by name, has no version compatibility issues, retained in Spring 6.

Constructor injection needs no annotation; avoid mixing with @Resource.

3.4 Circular‑dependency depth test

Scenario: A depends on B and B depends on A (bidirectional).

Field injection – starts normally, three‑level cache resolves the reference.

Setter injection – starts normally, three‑level cache resolves the reference.

Constructor injection – throws BeanCurrentlyInCreationException, cannot resolve.

Production solution: for bidirectional circular dependencies, split one side into a setter with optional dependency, prohibit field injection, thereby satisfying standards while breaking the cycle.

4. Why IDEA Highlights Field Injection

IDEA embeds Spring’s official coding rules, which sync with Spring 6 deprecation of field injection.

The yellow warning is a code‑quality risk indicator, not a compilation error.

Disabling the warning merely hides the risk; it does not eliminate potential production bugs.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

javaSpringDependency InjectionConstructor InjectionField InjectionSetter InjectionLombok
Java Tech Workshop
Written by

Java Tech Workshop

Focused on Java backend technologies, sharing fundamentals, multithreading, JVM, the Spring ecosystem, microservices, distributed systems, high concurrency, source‑code analysis, and practical experience. Continuously delivers high‑quality original content, interview guides, and learning roadmaps to help Java developers progress from beginner to advanced, enhancing technical skills and core competitiveness.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.