Anıl Şenocak Anıl Şenocak
Yazılım Mühendisi
Cover image
2026-07-11 0 toplam yorum
Why Should We Use Constructor Injection?

Dependency injection is an approach to implementing loose coupling between classes in an application. There are different ways to inject dependencies, and this article explains why constructor injection should be the preferred way.

What is dependency injection?

Dependency: An object usually needs objects of other classes to perform its operations. We call these objects dependencies.

Injection: The process of providing the required dependencies to an object.

Therefore, dependency injection helps implement inversion of control (IoC). This means that instead of the class itself creating the dependency objects, the responsibility of object creation and dependency injection is given to the framework (i.e., Spring).

We can implement dependency injection with:

  • constructor-based injection,
  • setter-based injection,
  • field-based injection.

Constructor Injection

The dependencies required for the class are provided to the constructor as arguments:

@Component
class Cake {

  private Flavor flavor;

  Cake(Flavor flavor) {
    Objects.requireNonNull(flavor);
    this.flavor = flavor;
  }

  Flavor getFlavor() {
    return flavor;
  }
  //...
}

Before Spring 4.3, we had to add an @Autowired annotation to the constructor. In newer versions, this is optional if the class has only one constructor. In the Cake class above, since we only have one constructor, we don't need to specify the @Autowired annotation. Consider the following example with two constructors:

@Component
class Sandwich {

  private Topping toppings;
  private Bread breadType;

  Sandwich(Topping toppings) {
    this.toppings = toppings;
  }

  @Autowired
  Sandwich(Topping toppings, Bread breadType) {
    this.toppings = toppings;
    this.breadType = breadType;
  }
  //...
}

When we have a class with multiple constructors, we must explicitly add the @Autowired annotation to one of the constructors so that Spring knows which constructor to use for injecting dependencies.

Setter Injection

We inject the required dependencies as field parameters into the class, and the values are set using the "setter" methods of the properties. We must annotate the setter methods with @Autowired. The Cookie class requires an object of type Topping. The Topping object is provided as an argument in the setter method of this property:

@Component
class Cookie {
  private Topping toppings;

  @Autowired
  void setTopping(Topping toppings) {
    this.toppings = toppings;
  }

  Topping getTopping() {
    return toppings;
  }
  //...
}

Spring will find the @Autowired annotation and call the setter to inject the dependency.

Field Injection

Spring assigns the required dependencies directly to the fields annotated with @Autowired. In this example, we let Spring inject the Topping dependency via field injection:

@Component
class IceCream {
  @Autowired
  private Topping toppings;

  Topping getToppings() {
    return toppings;
  }

  void setToppings(Topping toppings) {
    this.toppings = toppings;
  }
}

Combination of Field and Setter Injections

What happens if we add @Autowired to both a field and a setter? Which method will Spring use to inject the dependency?

@Component
class Pizza {
  @Autowired
  private Topping toppings;

  Topping getToppings() {
    return toppings;
  }

  @Autowired
  void setToppings(Topping toppings) {
    this.toppings = toppings;
  }
}

In the above example, we added the @Autowired annotation to both the "set" function and the field. In this case, Spring injects the dependency using the setter injection method. Keep in mind that mixing injection types within a single class is a bad practice as it makes the code less readable.

Why Should I Use Constructor Injection?

Now that we've seen the different injection types, let's go over some of the advantages of using constructor injection.

All Required Dependencies are Available at Initialization Time

We create the object when the constructor method is called. If the constructor method expects all required dependencies as parameters, then we can be 100% sure that the class will never be instantiated without its dependencies injected.

The IoC container ensures that all arguments provided to the constructor method are available before being passed to the constructor. This helps prevent the notorious NullPointerException.

Constructor injection is extremely useful because code complexity is simplified since we do not have to write separate business logic everywhere to check if all required dependencies have been loaded.

What About Optional Dependencies? With setter injection, Spring allows us to specify optional dependencies by adding @Autowired(required=false) to a setter method. This is not possible with constructor injection, as required=false would apply to all constructor arguments.

We can still provide optional dependencies with constructor injection by using Java's Optional type.

Identifying Code Smells

Constructor injection helps us identify if our beans depend on too many other objects. If our constructor has a large number of arguments, this might be a sign that our class has too many responsibilities. We might want to consider refactoring our code to better address the proper separation of concerns.

Preventing Errors in Tests

Constructor injection simplifies writing unit tests. The constructor forces us to provide valid objects for all dependencies. Using mocking libraries like Mockito, we can create mock objects that we can then pass to the constructor.

Of course, we can also provide mock data via setters, but if we add a new dependency to a class, we might forget to call the setter in the test, potentially causing a NullPointerException in the test. Constructor injection ensures that our test scenarios are executed only when all dependencies are present.

Immutability

Constructor injection helps create immutable objects because a constructor's signature is the only possible way to create the objects. Once we create a bean, we can no longer change its dependencies. With setter injection, it is possible to inject the dependency after creation, which leads to mutable objects that, among other things, may not be thread-safe in a multi-threaded environment and are harder to debug due to their mutability.

Conclusion

Constructor injection makes the code more robust. It allows us to create immutable objects, preventing NullPointerExceptions and other errors.

Yorumlar
Henüz yorum yok. İlk yorumu sen yaz.
Yorum Bırak
Adınız (isteğe bağlı)
Yorumunuzu yazın...
0/500